Initial Contribution

Signed-off-by: Jan Supol <jan.supol@oracle.com>
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
new file mode 100644
index 0000000..72d7b18
--- /dev/null
+++ b/tests/e2e-client/pom.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-client</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e-client</name>
+
+    <description>Jersey E2E Client tests</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-freemarker</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-mustache</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jdk-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-signature</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth2-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>xmlunit</groupId>
+            <artifactId>xmlunit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>xdk</id>
+            <properties>
+                <!-- do not use security manager for xdk -->
+                <surefire.security.argline />
+            </properties>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+    </profiles>
+
+</project>
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/AbortResponseClientTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/AbortResponseClientTest.java
new file mode 100644
index 0000000..d312a3c
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/AbortResponseClientTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests aborting the request on the client side.
+ *
+ * @author Miroslav Fuksa
+ */
+public class AbortResponseClientTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Test
+    public void testRequestAbort() {
+
+        final Date date = getDate();
+
+        ClientRequestFilter outFilter = new ClientRequestFilter() {
+
+            @Override
+            public void filter(ClientRequestContext context) throws IOException {
+                NewCookie cookie1 = new NewCookie("cookie1", "cookie1");
+                NewCookie cookie2 = new NewCookie("cookie2", "cookie2");
+                final Response response = Response.ok().cookie(cookie1).cookie(cookie2)
+                        .header("head1", "head1").header(HttpHeaders.DATE, date).header(HttpHeaders.ETAG,
+                                "\"123465\"").header(HttpHeaders.CONTENT_LANGUAGE, "language").header(HttpHeaders.LAST_MODIFIED,
+                                date).header(HttpHeaders.CONTENT_LENGTH, 99).type(MediaType.TEXT_HTML_TYPE)
+                        .location(URI.create("www.oracle.com")).build();
+
+                // abort the request
+                context.abortWith(response);
+            }
+        };
+        ClientResponseFilter inFilter = new ClientResponseFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext,
+                               ClientResponseContext responseContext) throws IOException {
+                Map<String, NewCookie> map = responseContext.getCookies();
+                assertEquals("cookie1", map.get("cookie1").getValue());
+                assertEquals("cookie2", map.get("cookie2").getValue());
+                final MultivaluedMap<String, String> headers = responseContext.getHeaders();
+                assertEquals("head1", headers.get("head1").get(0));
+                assertEquals(date.getTime(), responseContext.getDate().getTime());
+            }
+        };
+
+        WebTarget target = target().path("test");
+        target.register(outFilter).register(inFilter);
+        Invocation i = target.request().buildGet();
+        Response r = i.invoke();
+
+        assertEquals("head1", r.getHeaderString("head1"));
+        assertEquals("cookie1", r.getCookies().get("cookie1").getValue());
+        assertEquals("cookie2", r.getCookies().get("cookie2").getValue());
+        assertEquals(date.getTime(), r.getDate().getTime());
+        assertEquals("123465", r.getEntityTag().getValue());
+        assertEquals("language", r.getLanguage().toString());
+        assertEquals(date.getTime(), r.getLastModified().getTime());
+        // Assert.assertEquals("uri", r.getLink("link")); TODO: not supported yet
+        assertEquals("www.oracle.com", r.getLocation().toString());
+        assertEquals(MediaType.TEXT_HTML_TYPE, r.getMediaType());
+        assertEquals(99, r.getLength());
+
+        assertEquals(200, r.getStatus());
+    }
+
+    private Date getDate() {
+        Calendar cal = Calendar.getInstance();
+        cal.set(Calendar.YEAR, 2012);
+        cal.set(Calendar.MONTH, 7);
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+        cal.set(Calendar.HOUR, 10);
+        cal.set(Calendar.MINUTE, 5);
+        cal.set(Calendar.SECOND, 1);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
+
+    @Path("test")
+    public static class TestResource {
+
+        @GET
+        public String get() {
+            return "this will never be called.";
+        }
+
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/BasicClientTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/BasicClientTest.java
new file mode 100644
index 0000000..996adb1
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/BasicClientTest.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientAsyncExecutor;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.spi.ThreadPoolExecutorProvider;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static javax.ws.rs.client.Entity.text;
+
+/**
+ * Tests sync and async client invocations.
+ *
+ * @author Miroslav Fuksa
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class BasicClientTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        //        enable(TestProperties.LOG_TRAFFIC);
+        //        enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testCustomExecutorsAsync() throws ExecutionException, InterruptedException {
+        ClientConfig jerseyConfig = new ClientConfig();
+        jerseyConfig.register(CustomExecutorProvider.class).register(ThreadInterceptor.class);
+        Client client = ClientBuilder.newClient(jerseyConfig);
+        runCustomExecutorTestAsync(client);
+    }
+
+    @Test
+    public void testCustomExecutorsInstanceAsync() throws ExecutionException, InterruptedException {
+        ClientConfig jerseyConfig = new ClientConfig();
+        jerseyConfig.register(new CustomExecutorProvider()).register(ThreadInterceptor.class);
+        Client client = ClientBuilder.newClient(jerseyConfig);
+        runCustomExecutorTestAsync(client);
+    }
+
+    @Test
+    public void testCustomExecutorsSync() throws ExecutionException, InterruptedException {
+        ClientConfig jerseyConfig = new ClientConfig();
+        jerseyConfig.register(CustomExecutorProvider.class).register(ThreadInterceptor.class);
+        Client client = ClientBuilder.newClient(jerseyConfig);
+        runCustomExecutorTestSync(client);
+    }
+
+    @Test
+    public void testCustomExecutorsInstanceSync() throws ExecutionException, InterruptedException {
+        ClientConfig jerseyConfig = new ClientConfig();
+        jerseyConfig.register(new CustomExecutorProvider()).register(ThreadInterceptor.class);
+        Client client = ClientBuilder.newClient(jerseyConfig);
+        runCustomExecutorTestSync(client);
+    }
+
+    private void runCustomExecutorTestAsync(Client client) throws InterruptedException, ExecutionException {
+        AsyncInvoker async = client.target(getBaseUri()).path("resource").request().async();
+
+        Future<Response> f1 = async.post(text("post"));
+        final Response response = f1.get();
+        final String entity = response.readEntity(String.class);
+        assertEquals("custom-async-request-post", entity);
+    }
+
+    private void runCustomExecutorTestSync(Client client) {
+        WebTarget target = client.target(getBaseUri()).path("resource");
+        final Response response = target.request().post(text("post"));
+
+        final String entity = response.readEntity(String.class);
+        assertNotSame("custom-async-request-post", entity);
+    }
+
+    @Test
+    public void testAsyncClientInvocation() throws InterruptedException, ExecutionException {
+        final WebTarget resource = target().path("resource");
+
+        Future<Response> f1 = resource.request().async().post(text("post1"));
+        final Response response = f1.get();
+        assertEquals("post1", response.readEntity(String.class));
+
+        Future<String> f2 = resource.request().async().post(text("post2"), String.class);
+        assertEquals("post2", f2.get());
+
+        Future<List<JaxbString>> f3 = resource.request().async().get(new GenericType<List<JaxbString>>() {
+        });
+        assertEquals(
+                Arrays.asList("a", "b", "c").toString(),
+                f3.get().stream().map(input -> input.value).collect(Collectors.toList()).toString());
+
+        CompletableFuture<String> future1 = new CompletableFuture<>();
+        final TestCallback<Response> c1 = new TestCallback<Response>(future1) {
+
+            @Override
+            protected String process(Response result) {
+                return result.readEntity(String.class);
+            }
+        };
+
+        resource.request().async().post(text("post"), c1);
+        assertEquals("post", future1.get());
+
+        CompletableFuture<String> future2 = new CompletableFuture<>();
+        final TestCallback<String> c2 = new TestCallback<String>(future2) {
+
+            @Override
+            protected String process(String result) {
+                return result;
+            }
+        };
+        resource.request().async().post(text("post"), c2);
+        assertEquals("post", future2.get());
+
+        CompletableFuture<String> future3 = new CompletableFuture<>();
+        final TestCallback<List<JaxbString>> c3 = new TestCallback<List<JaxbString>>(future3) {
+
+            @Override
+            protected String process(List<JaxbString> result) {
+                return result.stream().map(jaxbString -> jaxbString.value).collect(Collectors.toList()).toString();
+            }
+        };
+        resource.request().async().get(c3);
+        assertEquals(Arrays.asList("a", "b", "c").toString(), future3.get());
+    }
+
+    @Test
+    public void testAsyncClientInvocationErrorResponse() throws InterruptedException, ExecutionException {
+        final WebTarget errorResource = target().path("resource").path("error");
+
+        Future<Response> f1 = errorResource.request().async().get();
+        final Response response = f1.get();
+        assertEquals(404, response.getStatus());
+        assertEquals("error", response.readEntity(String.class));
+
+        Future<String> f2 = errorResource.request().async().get(String.class);
+        try {
+            f2.get();
+            fail("ExecutionException expected.");
+        } catch (ExecutionException ex) {
+            Throwable cause = ex.getCause();
+            assertTrue(WebApplicationException.class.isAssignableFrom(cause.getClass()));
+            final Response r = ((WebApplicationException) cause).getResponse();
+            assertEquals(404, r.getStatus());
+            assertEquals("error", r.readEntity(String.class));
+        }
+
+        Future<List<String>> f3 = target().path("resource").path("errorlist").request()
+                                          .async().get(new GenericType<List<String>>() {
+                });
+        try {
+            f3.get();
+            fail("ExecutionException expected.");
+        } catch (ExecutionException ex) {
+            Throwable cause = ex.getCause();
+            assertTrue(WebApplicationException.class.isAssignableFrom(cause.getClass()));
+            final Response r = ((WebApplicationException) cause).getResponse();
+            assertEquals(404, r.getStatus());
+            assertEquals("error", r.readEntity(String.class));
+        }
+
+        CompletableFuture<String> future1 = new CompletableFuture<>();
+        final TestCallback<Response> c1 = new TestCallback<Response>(future1) {
+
+            @Override
+            protected String process(Response result) {
+                return result.readEntity(String.class);
+            }
+        };
+        errorResource.request().async().get(c1);
+        assertEquals("error", future1.get());
+
+        CompletableFuture<String> future2 = new CompletableFuture<>();
+        final TestCallback<String> c2 = new TestCallback<String>(future2) {
+
+            @Override
+            protected String process(String result) {
+                return result;
+            }
+        };
+        errorResource.request().async().get(c2);
+        try {
+            future2.get();
+            fail("ExecutionException expected.");
+        } catch (ExecutionException ex) {
+            Throwable cause = ex.getCause();
+            assertTrue(WebApplicationException.class.isAssignableFrom(cause.getClass()));
+            final Response r = ((WebApplicationException) cause).getResponse();
+            assertEquals(404, r.getStatus());
+            assertEquals("error", r.readEntity(String.class));
+        }
+
+        CompletableFuture<String> future3 = new CompletableFuture<>();
+        final TestCallback<List<JaxbString>> c3 = new TestCallback<List<JaxbString>>(future3) {
+
+            @Override
+            protected String process(List<JaxbString> result) {
+                return result.stream().map(jaxbString -> jaxbString.value).collect(Collectors.toList()).toString();
+            }
+        };
+        target().path("resource").path("errorlist").request().async().get(c3);
+        try {
+            future3.get();
+            fail("ExecutionException expected.");
+        } catch (ExecutionException ex) {
+            Throwable cause = ex.getCause();
+            assertTrue(WebApplicationException.class.isAssignableFrom(cause.getClass()));
+            final Response r = ((WebApplicationException) cause).getResponse();
+            assertEquals(404, r.getStatus());
+            assertEquals("error", r.readEntity(String.class));
+        }
+    }
+
+    @Test
+    public void testSyncClientInvocation() throws InterruptedException, ExecutionException {
+        final WebTarget resource = target().path("resource");
+
+        Response r1 = resource.request().post(text("post1"));
+        assertEquals("post1", r1.readEntity(String.class));
+
+        String r2 = resource.request().post(text("post2"), String.class);
+        assertEquals("post2", r2);
+
+        List<JaxbString> r3 = resource.request().get(new GenericType<List<JaxbString>>() {
+        });
+        assertEquals(Arrays.asList("a", "b", "c").toString(),
+                     r3.stream().map(input -> input.value).collect(Collectors.toList()).toString());
+    }
+
+    @Test
+    public void testSyncClientInvocationErrorResponse() throws InterruptedException, ExecutionException {
+        final WebTarget errorResource = target().path("resource").path("error");
+
+        Response r1 = errorResource.request().get();
+        assertEquals(404, r1.getStatus());
+        assertEquals("error", r1.readEntity(String.class));
+
+        try {
+            errorResource.request().get(String.class);
+            fail("ExecutionException expected.");
+        } catch (WebApplicationException ex) {
+            final Response r = ex.getResponse();
+            assertEquals(404, r.getStatus());
+            assertEquals("error", r.readEntity(String.class));
+        }
+
+        try {
+            target().path("resource").path("errorlist").request().get(new GenericType<List<String>>() {
+            });
+            fail("ExecutionException expected.");
+        } catch (WebApplicationException ex) {
+            final Response r = ex.getResponse();
+            assertEquals(404, r.getStatus());
+            assertEquals("error", r.readEntity(String.class));
+        }
+    }
+
+    @Test
+    // JERSEY-1412
+    public void testAbortAsyncRequest() throws Exception {
+        Invocation invocation = abortingTarget().request().buildPost(text("entity"));
+        Future<String> future = invocation.submit(String.class);
+        assertEquals("aborted", future.get());
+    }
+
+    @Test
+    // JERSEY-1412
+    public void testAbortSyncRequest() throws Exception {
+        Invocation invocation = abortingTarget().request().buildPost(text("entity"));
+        String response = invocation.invoke(String.class);
+        assertEquals("aborted", response);
+    }
+
+    protected WebTarget abortingTarget() {
+        Client client = ClientBuilder.newClient();
+        client.register(new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext ctx) throws IOException {
+                Response r = Response.ok("aborted").build();
+                ctx.abortWith(r);
+            }
+        });
+        return client.target("http://any.web:888");
+    }
+
+    @ClientAsyncExecutor
+    public static class CustomExecutorProvider extends ThreadPoolExecutorProvider {
+
+        /**
+         * Create a new instance of the thread pool executor provider.
+         */
+        public CustomExecutorProvider() {
+            super("custom-async-request");
+        }
+    }
+
+    public static class ThreadInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final String name = Thread.currentThread().getName(); // e.g. custom-async-request-0
+            final int lastIndexOfDash = name.lastIndexOf('-');
+            context.setEntity(
+                    name.substring(0, lastIndexOfDash < 0 ? name.length() : lastIndexOfDash) + "-" + context.getEntity());
+            context.proceed();
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        @Path("error")
+        public String getError() {
+            throw new NotFoundException(Response.status(404).type("text/plain").entity("error").build());
+        }
+
+        @GET
+        @Path("errorlist")
+        @Produces(MediaType.APPLICATION_XML)
+        public List<JaxbString> getErrorList() {
+            throw new NotFoundException(Response.status(404).type("text/plain").entity("error").build());
+        }
+
+        @GET
+        @Produces(MediaType.APPLICATION_XML)
+        public List<JaxbString> get() {
+            return Arrays.asList(new JaxbString("a"), new JaxbString("b"), new JaxbString("c"));
+        }
+
+        @POST
+        public String post(String entity) {
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            return entity;
+        }
+    }
+
+    private abstract static class TestCallback<T> implements InvocationCallback<T> {
+
+        private final CompletableFuture<String> completableFuture;
+
+        TestCallback(CompletableFuture<String> completableFuture) {
+            this.completableFuture = completableFuture;
+        }
+
+        @Override
+        public void completed(T result) {
+            try {
+                completableFuture.complete(process(result));
+            } catch (Throwable t) {
+                completableFuture.completeExceptionally(t);
+            }
+        }
+
+        protected abstract String process(T result);
+
+        @Override
+        public void failed(Throwable error) {
+            if (error.getCause() instanceof WebApplicationException) {
+                completableFuture.completeExceptionally(error.getCause());
+            } else {
+                completableFuture.completeExceptionally((error));
+            }
+        }
+    }
+
+    @XmlRootElement
+    public static class JaxbString {
+
+        public String value;
+
+        public JaxbString() {
+        }
+
+        public JaxbString(String value) {
+            this.value = value;
+        }
+    }
+}
+
+
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/BufferingTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/BufferingTest.java
new file mode 100644
index 0000000..a691fc8
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/BufferingTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests chunk encoding and possibility of buffering the entity.
+ *
+ * @author Miroslav Fuksa
+ */
+public class BufferingTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class, LoggingFeature.class);
+    }
+
+    @Path("resource")
+    public static class MyResource {
+        @POST
+        public String getBuffered(@HeaderParam("content-length") String contentLenght,
+                                  @HeaderParam("transfer-encoding") String transferEncoding) {
+            if (transferEncoding != null && transferEncoding.equals("chunked")) {
+                return "chunked";
+            }
+            return contentLenght;
+        }
+    }
+
+    @Test
+    public void testApacheConnector() {
+        testWithBuffering(getApacheConnectorConfig());
+        testWithChunkEncodingWithoutPropertyDefinition(getApacheConnectorConfig());
+        testWithChunkEncodingWithPropertyDefinition(getApacheConnectorConfig());
+        testWithChunkEncodingPerRequest(getApacheConnectorConfig());
+        testDefaultOption(getApacheConnectorConfig(), RequestEntityProcessing.CHUNKED);
+    }
+
+    @Test
+    public void testGrizzlyConnector() {
+        testWithBuffering(getGrizzlyConnectorConfig());
+        testWithChunkEncodingWithoutPropertyDefinition(getGrizzlyConnectorConfig());
+        testWithChunkEncodingWithPropertyDefinition(getGrizzlyConnectorConfig());
+        testWithChunkEncodingPerRequest(getGrizzlyConnectorConfig());
+        testDefaultOption(getGrizzlyConnectorConfig(), RequestEntityProcessing.CHUNKED);
+    }
+
+    @Test
+    public void testHttpUrlConnector() {
+        testWithBuffering(getHttpUrlConnectorConfig());
+        testWithChunkEncodingWithoutPropertyDefinition(getHttpUrlConnectorConfig());
+        testWithChunkEncodingWithPropertyDefinition(getHttpUrlConnectorConfig());
+        testWithChunkEncodingPerRequest(getHttpUrlConnectorConfig());
+        testDefaultOption(getHttpUrlConnectorConfig(), RequestEntityProcessing.BUFFERED);
+    }
+
+    private ClientConfig getApacheConnectorConfig() {
+        return new ClientConfig().connectorProvider(new ApacheConnectorProvider());
+    }
+
+    private ClientConfig getGrizzlyConnectorConfig() {
+        return new ClientConfig().connectorProvider(new GrizzlyConnectorProvider());
+    }
+
+    private ClientConfig getHttpUrlConnectorConfig() {
+        return new ClientConfig();
+    }
+
+    private void testDefaultOption(ClientConfig cc, RequestEntityProcessing mode) {
+        String entity = getVeryLongString();
+        makeRequest(cc, entity, mode == RequestEntityProcessing.BUFFERED ? String.valueOf(entity.length())
+                : "chunked");
+    }
+
+    private void testWithBuffering(ClientConfig cc) {
+        cc.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
+        String entity = getVeryLongString();
+
+        makeRequest(cc, entity, String.valueOf(entity.length()));
+    }
+
+    private void makeRequest(ClientConfig cc, String entity, String expected) {
+        Client client = ClientBuilder.newClient(cc);
+        WebTarget target = client.target(UriBuilder.fromUri(getBaseUri()).path("resource").build());
+
+        Response response = target.request().post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(expected, response.readEntity(String.class));
+    }
+
+    private void testWithChunkEncodingWithPropertyDefinition(ClientConfig cc) {
+        cc.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
+        cc.property(ClientProperties.CHUNKED_ENCODING_SIZE, 3000);
+        String entity = getVeryLongString();
+
+        makeRequest(cc, entity, "chunked");
+    }
+
+    private void testWithChunkEncodingWithoutPropertyDefinition(ClientConfig cc) {
+        cc.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
+        String entity = getVeryLongString();
+
+        makeRequest(cc, entity, "chunked");
+    }
+
+
+    /**
+     * Tests that {@link org.glassfish.jersey.client.ClientProperties#REQUEST_ENTITY_PROCESSING} can be defined
+     * per request with different values.
+     */
+    private void testWithChunkEncodingPerRequest(ClientConfig cc) {
+        cc.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
+
+        cc.property(ClientProperties.CHUNKED_ENCODING_SIZE, 3000);
+        Client client = ClientBuilder.newClient(cc);
+        WebTarget target = client.target(UriBuilder.fromUri(getBaseUri()).path("resource").build());
+
+        String entity = getVeryLongString();
+        Response response = target.request().post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("chunked", response.readEntity(String.class));
+
+        response = target.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED)
+                .request().post(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(String.valueOf(entity.length()), response.readEntity(String.class));
+    }
+
+    public String getVeryLongString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < 1; i++) {
+            sb.append("helllllloooooooooooooooooooooooooooooouuuuuuuuuuu.");
+        }
+        return sb.toString();
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/CancelFutureClientTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/CancelFutureClientTest.java
new file mode 100644
index 0000000..af0ade7
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/CancelFutureClientTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 2018 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.client;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests the behaviour of the Async client when the {@link java.util.concurrent.Future} is cancelled.
+ *
+ * <p>
+ * Tests, that if the async request future is cancelled by the client,
+ * the {@link javax.ws.rs.client.InvocationCallback#completed(Object)} callback is not invoked and that
+ * {@link java.util.concurrent.CancellationException} is correctly returned (according to spec.) to
+ * {@link javax.ws.rs.client.InvocationCallback#failed(Throwable)} callback method.
+ * </p>
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class CancelFutureClientTest extends JerseyTest {
+
+    public static final long MAX_WAITING_SECONDS = 2L;
+    final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Test
+    public void testCancelFuture() throws InterruptedException, TimeoutException {
+        Future<Response> future = target().path("test").request().async().get(
+                new InvocationCallback<Response>() {
+                    public void completed(final Response response) {
+                        fail("[completed()] callback was invoked, although the Future should have been cancelled.");
+                    }
+
+                    public void failed(final Throwable throwable) {
+                        assertEquals(CancellationException.class, throwable.getClass());
+                        countDownLatch.countDown();
+                    }
+                }
+        );
+        if (!future.cancel(true)) {
+            fail("The Future could not be canceled.");
+        }
+
+        // prevent the test container to stop the method execution before the callbacks can be reached.
+        if (!countDownLatch.await(MAX_WAITING_SECONDS * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS)) {
+            throw new TimeoutException("Callback was not triggered within the time limit." + countDownLatch.getCount());
+        }
+    }
+
+    @Path("test")
+    public static class TestResource {
+        @GET
+        public Response get() {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            return Response.noContent().build();
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ChunkedInputStreamClosedPrematurelyTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ChunkedInputStreamClosedPrematurelyTest.java
new file mode 100644
index 0000000..11f5c31
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ChunkedInputStreamClosedPrematurelyTest.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2014, 2018 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Reproducer for JERSEY-2705. Client side entity InputStream exception
+ * in chunked mode should not lead to the same behavior on the server side,
+ * as if no exception occurred at all.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ChunkedInputStreamClosedPrematurelyTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(ChunkedInputStreamClosedPrematurelyTest.class.getName());
+    private static final Exception NO_EXCEPTION = new Exception("No exception.");
+
+    private static final AtomicInteger NEXT_REQ_ID = new AtomicInteger(0);
+    private static final String REQ_ID_PARAM_NAME = "test-req-id";
+    private static final int BYTES_TO_SEND = 1024 * 1024 + 13;
+
+    @Path("/test")
+    @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "JavaDoc"})
+    public static class TestResource {
+
+        private static final ConcurrentMap<String, SettableFuture<Exception>> REQUEST_MAP = new ConcurrentHashMap<>();
+
+        @QueryParam(REQ_ID_PARAM_NAME)
+        private String reqId;
+
+        @POST
+        public String post(InputStream is) {
+            final byte[] buffer = new byte[4096];
+            int readTotal = 0;
+
+            Exception thrown = NO_EXCEPTION;
+            try {
+                int read;
+                while ((read = is.read(buffer)) > -1) {
+                    readTotal += read;
+                }
+            } catch (Exception ex) {
+                thrown = ex;
+            }
+
+            if (!getFutureFor(reqId).set(thrown)) {
+                LOGGER.log(Level.WARNING,
+                        "Unable to set stream processing exception into the settable future instance for request id " + reqId,
+                        thrown);
+            }
+
+            return Integer.toString(readTotal);
+        }
+
+        @Path("/requestWasMade")
+        @GET
+        public Boolean getRequestWasMade() {
+            // add a new future for the request if not there yet to avoid race conditions with POST processing
+            final SettableFuture<Exception> esf = getFutureFor(reqId);
+            try {
+                // wait for up to three second for a request to be made;
+                // there is always a value, if set...
+                return esf.get(3, TimeUnit.SECONDS) != null;
+            } catch (InterruptedException | TimeoutException | ExecutionException e) {
+                throw new InternalServerErrorException("Post request processing has timed out for request id " + reqId, e);
+            }
+        }
+
+        @Path("/requestCausedException")
+        @GET
+        public Boolean getRequestCausedException() {
+            final SettableFuture<Exception> esf = getFutureFor(reqId);
+            try {
+                return esf.get(3, TimeUnit.SECONDS) != NO_EXCEPTION;
+            } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                throw new InternalServerErrorException("Post request processing has timed out for request id " + reqId, e);
+            }
+        }
+
+        private SettableFuture<Exception> getFutureFor(String key) {
+            final SettableFuture<Exception> esf = SettableFuture.create();
+            final SettableFuture<Exception> oldEsf = REQUEST_MAP.putIfAbsent(key, esf);
+            return (oldEsf != null) ? oldEsf : esf;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
+        config.property(ClientProperties.CHUNKED_ENCODING_SIZE, 7);
+    }
+
+    /**
+     * A sanity test to check the normal use case is working as expected.
+     */
+    @Test
+    public void testUninterrupted() {
+        final String testReqId = nextRequestId("testUninterrupted");
+
+        Response testResponse = target("test").queryParam(REQ_ID_PARAM_NAME, testReqId)
+                .request().post(Entity.entity("0123456789ABCDEF", MediaType.APPLICATION_OCTET_STREAM));
+        assertEquals("Unexpected response status code.", 200, testResponse.getStatus());
+        assertEquals("Unexpected response entity.", "16", testResponse.readEntity(String.class));
+
+        assertTrue("POST request " + testReqId + " has not reached the server.",
+                target("test").path("requestWasMade").queryParam(REQ_ID_PARAM_NAME, testReqId)
+                        .request().get(Boolean.class));
+        assertFalse("POST request " + testReqId + " has caused an unexpected exception on the server.",
+                target("test").path("requestCausedException").queryParam(REQ_ID_PARAM_NAME, testReqId)
+                        .request().get(Boolean.class));
+    }
+
+    /**
+     * This test simulates how Jersey Client should behave after JERSEY-2705 gets fixed.
+     *
+     * @throws Exception in case the test fails to execute.
+     */
+    @Test
+    public void testInterruptedJerseyHttpUrlConnection() throws Exception {
+
+        final String testReqId = nextRequestId("testInterruptedJerseyHttpUrlConnection");
+
+        URL postUrl = UriBuilder.fromUri(getBaseUri()).path("test").queryParam(REQ_ID_PARAM_NAME, testReqId).build().toURL();
+        final HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
+
+        try {
+            connection.setRequestMethod("POST");
+            connection.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM);
+            connection.setDoOutput(true);
+            connection.setChunkedStreamingMode(1024);
+            OutputStream entityStream = connection.getOutputStream();
+            ReaderWriter.writeTo(new ExceptionThrowingInputStream(BYTES_TO_SEND), entityStream);
+            Assert.fail("Expected ProcessingException has not been thrown.");
+        } catch (IOException expected) {
+            // so far so good
+        } finally {
+            connection.disconnect();
+        }
+        // we should make it to the server, but there the exceptional behaviour should get noticed
+        assertTrue("POST request " + testReqId + " has not reached the server.",
+                target("test").path("requestWasMade").queryParam(REQ_ID_PARAM_NAME, testReqId).request().get(Boolean.class));
+        assertTrue("POST request " + testReqId + " did not cause an expected exception on the server.",
+                target("test").path("requestCausedException").queryParam(REQ_ID_PARAM_NAME, testReqId)
+                        .request().get(Boolean.class));
+    }
+
+    /**
+     * This test reproduces the Jersey Client behavior reported in JERSEY-2705.
+     */
+    @Ignore
+    @Test
+    public void testInterruptedJerseyClient() {
+        final String testReqId = nextRequestId("testInterruptedJerseyClient");
+
+        try {
+            target("test").queryParam(REQ_ID_PARAM_NAME, testReqId).request()
+                    .post(Entity.entity(new ExceptionThrowingInputStream(BYTES_TO_SEND), MediaType.APPLICATION_OCTET_STREAM));
+            Assert.fail("Expected ProcessingException has not been thrown.");
+        } catch (ProcessingException expected) {
+            // so far so good
+        }
+        // we should make it to the server, but there the exceptional behaviour should get noticed
+        assertTrue("POST request " + testReqId + " has not reached the server.",
+                target("test").path("requestWasMade").queryParam(REQ_ID_PARAM_NAME, testReqId).request().get(Boolean.class));
+        assertTrue("POST request " + testReqId + " did not cause an expected exception on the server.",
+                target("test").path("requestCausedException").queryParam(REQ_ID_PARAM_NAME, testReqId)
+                        .request().get(Boolean.class));
+    }
+
+    private static String nextRequestId(String testMethodName) {
+        return String.format(testMethodName + "-%03d", NEXT_REQ_ID.getAndIncrement());
+    }
+
+    /**
+     * InputStream implementation that allows "reading" as many bytes as specified by threshold constructor parameter.
+     * Throws an IOException if read operation is attempted after the threshold is exceeded.
+     */
+    private class ExceptionThrowingInputStream extends InputStream {
+
+        private final int threshold;
+        private int offset = 0;
+
+        /**
+         * Get me a new stream that throws exception.
+         *
+         * @param threshold this number of bytes will be read all right
+         */
+        public ExceptionThrowingInputStream(int threshold) {
+            this.threshold = threshold;
+        }
+
+        @Override
+        public int read() throws IOException {
+            if (offset++ < threshold) {
+                return 'A';
+            } else {
+                throw new IOException("stream closed");
+            }
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            offset += len;
+            if (offset < threshold) {
+                Arrays.fill(b, off, off + len, (byte) 'A');
+                return len;
+            } else {
+                throw new IOException("Stream closed");
+            }
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientBufferingDisabledTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientBufferingDisabledTest.java
new file mode 100644
index 0000000..4a21864
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientBufferingDisabledTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests possibility of disabling buffering of outgoing entity in
+ * {@link org.glassfish.jersey.client.HttpUrlConnectorProvider}.
+ *
+ * @author Miroslav Fuksa
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@RunWith(ConcurrentRunner.class)
+public class ClientBufferingDisabledTest extends JerseyTest {
+
+    private static final long LENGTH = 200000000L;
+    private static final int CHUNK = 2048;
+    private static CountDownLatch postLatch = new CountDownLatch(1);
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class);
+    }
+
+    @Path("resource")
+    public static class MyResource {
+
+        @POST
+        public long post(InputStream is) throws IOException {
+            int b;
+            long count = 0;
+            boolean firstByte = true;
+            while ((b = is.read()) != -1) {
+                if (firstByte) {
+                    firstByte = false;
+                    postLatch.countDown();
+                }
+
+                count++;
+            }
+            return count;
+        }
+    }
+
+
+    /**
+     * Test that buffering can be disabled with {@link HttpURLConnection}. By default, the
+     * {@code HttpURLConnection} buffers the output entity in order to calculate the
+     * Content-length request attribute. This cause problems for large entities.
+     * <p>
+     * This test uses {@link HttpUrlConnectorProvider#USE_FIXED_LENGTH_STREAMING} to enable
+     * fix length streaming on {@code HttpURLConnection}.
+     */
+    @Test
+    public void testDisableBufferingWithFixedLengthViaProperty() {
+        postLatch = new CountDownLatch(1);
+
+        // This IS sends out 10 chunks and waits whether they were received on the server. This tests
+        // whether the buffering is disabled.
+        InputStream is = getInputStream();
+
+        final HttpUrlConnectorProvider connectorProvider = new HttpUrlConnectorProvider();
+        ClientConfig clientConfig = new ClientConfig().connectorProvider(connectorProvider);
+        clientConfig.property(HttpUrlConnectorProvider.USE_FIXED_LENGTH_STREAMING, true);
+        Client client = ClientBuilder.newClient(clientConfig);
+        final Response response
+                = client.target(getBaseUri()).path("resource")
+                .request().header(HttpHeaders.CONTENT_LENGTH, LENGTH).post(
+                        Entity.entity(is, MediaType.APPLICATION_OCTET_STREAM));
+        Assert.assertEquals(200, response.getStatus());
+        final long count = response.readEntity(long.class);
+        Assert.assertEquals("Unexpected content length received.", LENGTH, count);
+    }
+
+    /**
+     * Test that buffering can be disabled with {@link HttpURLConnection}. By default, the
+     * {@code HttpURLConnection} buffers the output entity in order to calculate the
+     * Content-length request attribute. This cause problems for large entities.
+     * <p>
+     * This test uses {@link HttpUrlConnectorProvider#useFixedLengthStreaming()} to enable
+     * fix length streaming on {@code HttpURLConnection}.
+     */
+    @Test
+    public void testDisableBufferingWithFixedLengthViaMethod() {
+        postLatch = new CountDownLatch(1);
+
+        // This IS sends out 10 chunks and waits whether they were received on the server. This tests
+        // whether the buffering is disabled.
+        InputStream is = getInputStream();
+
+        final HttpUrlConnectorProvider connectorProvider = new HttpUrlConnectorProvider()
+                .useFixedLengthStreaming();
+        ClientConfig clientConfig = new ClientConfig().connectorProvider(connectorProvider);
+        Client client = ClientBuilder.newClient(clientConfig);
+        final Response response
+                = client.target(getBaseUri()).path("resource")
+                .request().header(HttpHeaders.CONTENT_LENGTH, LENGTH).post(
+                        Entity.entity(is, MediaType.APPLICATION_OCTET_STREAM));
+        Assert.assertEquals(200, response.getStatus());
+        final long count = response.readEntity(long.class);
+        Assert.assertEquals("Unexpected content length received.", LENGTH, count);
+    }
+
+    /**
+     * Test that buffering can be disabled with {@link HttpURLConnection}. By default, the
+     * {@code HttpURLConnection} buffers the output entity in order to calculate the
+     * Content-length request attribute. This cause problems for large entities.
+     * <p>
+     * In Jersey 1.x chunk encoding with {@code HttpURLConnection} was causing bugs
+     * which occurred from time to time. This looks to be a case also in Jersey 2.x. This test
+     * has failed unpredictably on some machines. Therefore it is disabled now.
+     * </p>
+     */
+    @Test
+    @Ignore("fails unpredictable (see javadoc)")
+    public void testDisableBufferingWithChunkEncoding() {
+        postLatch = new CountDownLatch(1);
+
+        // This IS sends out 10 chunks and waits whether they were received on the server. This tests
+        // whether the buffering is disabled.
+        InputStream is = getInputStream();
+
+        final HttpUrlConnectorProvider connectorProvider = new HttpUrlConnectorProvider()
+                .chunkSize(CHUNK);
+        ClientConfig clientConfig = new ClientConfig()
+                .connectorProvider(connectorProvider);
+        Client client = ClientBuilder.newClient(clientConfig);
+        final Response response
+                = client.target(getBaseUri()).path("resource")
+                .request().post(Entity.entity(is, MediaType.APPLICATION_OCTET_STREAM));
+        Assert.assertEquals(200, response.getStatus());
+        final long count = response.readEntity(long.class);
+        Assert.assertEquals("Unexpected content length received.", LENGTH, count);
+    }
+
+    private InputStream getInputStream() {
+        return new InputStream() {
+            private int cnt = 0;
+
+            @Override
+            public int read() throws IOException {
+                cnt++;
+                if (cnt > CHUNK * 10) {
+                    try {
+                        postLatch.await(3 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+                        Assert.assertEquals("waiting for chunk on the server side time-outed", 0, postLatch.getCount());
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                if (cnt <= LENGTH) {
+                    return 'a';
+                } else {
+                    return -1;
+                }
+            }
+        };
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientDestroyTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientDestroyTest.java
new file mode 100644
index 0000000..9997a12
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientDestroyTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2014, 2018 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.client;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+import javax.annotation.PreDestroy;
+
+import org.glassfish.jersey.client.ClientLifecycleListener;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.JerseyClientBuilder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Assert that pre destroy method on providers is invoked.
+ *
+ * @author Michal Gajdos
+ */
+public class ClientDestroyTest extends JerseyTest {
+
+    private static final Map<String, Boolean> destroyed = new HashMap<>();
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        destroyed.clear();
+        destroyed.put("filter", false);
+        destroyed.put("reader", false);
+        destroyed.put("feature", false);
+
+        super.setUp();
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String get(@HeaderParam("foo") final String foo) {
+            return "resource-" + foo;
+        }
+    }
+
+    public static class MyFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(final ClientRequestContext requestContext) throws IOException {
+            requestContext.getHeaders().putSingle("foo", "bar");
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("filter", true);
+        }
+    }
+
+    public static class MyReader implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final Object entity = context.proceed();
+            return "reader-" + entity;
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("reader", true);
+        }
+    }
+
+    public static class MyFeature implements Feature {
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("feature", true);
+        }
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            return true;
+        }
+    }
+
+    @Test
+    public void testClientInvokePreDestroyMethodOnProviderClass() throws Exception {
+        final Client client = ClientBuilder.newClient()
+                .register(MyFilter.class)
+                .register(MyReader.class)
+                .register(MyFeature.class);
+
+        assertThat(client.target(getBaseUri()).request().get(String.class), is("reader-resource-bar"));
+
+        checkDestroyed(false);
+        client.close();
+        checkDestroyed(true);
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    private void checkDestroyed(final boolean shouldBeDestroyed) {
+        for (final Map.Entry<String, Boolean> entry : destroyed.entrySet()) {
+            assertThat(entry.getKey() +  " should" + (shouldBeDestroyed ? "" : " not") + " be destroyed",
+                    entry.getValue(), is(shouldBeDestroyed));
+        }
+    }
+
+    public static class FooListener implements ClientRequestFilter, ClientLifecycleListener {
+        public static volatile boolean INITIALIZED = false;
+        public static volatile boolean CLOSED = false;
+
+        @Override
+        public void filter(final ClientRequestContext requestContext) throws IOException { /* do nothing */ }
+
+        @Override
+        public void onClose() {
+            CLOSED = true;
+        }
+
+        @Override
+        public void onInit() {
+            INITIALIZED = true;
+        }
+
+        // to check if closing works also for class-registered providers
+        public static boolean isClosed() {
+            return CLOSED;
+        }
+
+        public static boolean isInitialized() {
+            return INITIALIZED;
+        }
+    }
+
+    public static class BarListener implements ClientRequestFilter, ClientLifecycleListener {
+        protected volatile boolean closed = false;
+        protected volatile boolean initialized = false;
+
+        @Override
+        public void filter(final ClientRequestContext requestContext) throws IOException { /* do nothing */ }
+
+        @Override
+        public void onInit() {
+            this.initialized = true;
+        }
+
+        @Override
+        public synchronized void onClose() {
+            this.closed = true;
+        }
+
+        public boolean isClosed() {
+            return closed;
+        }
+
+        public boolean isInitialized() {
+            return initialized;
+        }
+    }
+
+    // another type needed, as multiple registrations of the same type are not allowed
+    public static class BarListener2 extends BarListener {
+    }
+
+    @Test
+    public void testLifecycleListenerProvider() {
+        final JerseyClientBuilder builder = new JerseyClientBuilder();
+        final JerseyClient client = builder.build();
+
+        final BarListener filter = new BarListener();
+        final BarListener filter2 = new BarListener2();
+
+        // ClientRuntime initializes lazily, so it is forced by invoking a (dummy) request
+        client.register(filter2);                                                   // instance registered into client
+        client.target(getBaseUri()).register(filter).request().get(String.class);   // instance registration into target
+
+        assertTrue("Filter was expected to be already initialized.", filter.isInitialized());
+        assertTrue("Filter2 was expected to be already initialized.", filter2.isInitialized());
+
+        client.target(getBaseUri()).register(FooListener.class).request().get(String.class); // class registration into target
+
+        assertTrue("Class-registered filter was expected to be already initialized", FooListener.isInitialized());
+
+        assertFalse("Class-registered filter was expected to be still open.", FooListener.isClosed());
+        assertFalse("Filter was expected to be still open.", filter.isClosed());
+        assertFalse("Filter2 was expected to be still open.", filter2.isClosed());
+
+        client.close();
+
+        assertTrue("Class-registered filter was expected to be closed.", FooListener.isClosed());
+        assertTrue("Filter was expected to be closed.", filter.isClosed());
+        assertTrue("Filter2 was expected to be closed.", filter2.isClosed());
+    }
+
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientEntityAnnotationTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientEntityAnnotationTest.java
new file mode 100644
index 0000000..79288c7
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientEntityAnnotationTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests annotations of entity on the client side.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class ClientEntityAnnotationTest extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(ClientFilter.class);
+    }
+
+    @Test
+    public void test() {
+        Annotation[] annotations = MyProvider.class.getAnnotations();
+        Entity<String> post = Entity.entity("test", MediaType.WILDCARD_TYPE,
+                annotations);
+        final Response response = target().path("resource").request().post(post);
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("test", response.readEntity(String.class));
+    }
+
+
+    public static class ClientFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            final Annotation[] entityAnnotations = requestContext.getEntityAnnotations();
+            Assert.assertEquals(1, entityAnnotations.length);
+            Assert.assertEquals(MyProvider.class.getAnnotation(Provider.class), entityAnnotations[0]);
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+        @POST
+        public String post(String entity) {
+            return entity;
+        }
+    }
+
+    @Provider
+    public static class MyProvider {
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorCloseTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorCloseTest.java
new file mode 100644
index 0000000..76c2d3f
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorCloseTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015, 2018 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.client;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.sse.SseEventSource;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Petr Janouch (petr.janouch at oracle.com)
+ */
+public class ClientExecutorCloseTest extends JerseyTest {
+    private static CountDownLatch cdl = new CountDownLatch(2);
+    private static boolean schedulerFound = false;
+
+    /**
+     * Tests that closing a client shuts down a corresponding client async executor service.
+     */
+    @Test
+    @Ignore("Jersey uses ForkJoin common pool by default, which shouldn't be closed when client closes.")
+    public void testCloseAsyncExecutor() throws InterruptedException {
+        assertFalse(clientExecutorThreadPresent());
+        target("resource").request().async().get();
+        final SseEventSource eventSource = SseEventSource
+                .target(target("resource/fail"))
+                .reconnectingEvery(11, TimeUnit.MILLISECONDS)
+                .build();
+        eventSource.register(System.out::println);
+        eventSource.open();
+        assertTrue("Waiting for eventSource to open time-outed", cdl.await(5000, TimeUnit.MILLISECONDS));
+        assertTrue("Client async executor thread not found.", clientExecutorThreadPresent());
+        assertTrue("Scheduler thread not found.", schedulerFound);
+        client().close();
+        assertFalse("Client async executor thread should have been already removed.",
+                clientExecutorThreadPresent());
+        assertFalse("Client background scheduler thread should have been already removed.",
+                clientSchedulerThreadPresent());
+    }
+
+    private boolean clientExecutorThreadPresent() {
+        Set<Thread> threads = Thread.getAllStackTraces().keySet();
+        return threads.stream().map(Thread::getName).anyMatch(name -> name.contains("jersey-client-async-executor"));
+    }
+
+    private static boolean clientSchedulerThreadPresent() {
+        Set<Thread> threads = Thread.getAllStackTraces().keySet();
+        for (Thread thread : threads) {
+            if (thread.getName().contains("jersey-client-background-scheduler")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        public String getHello() {
+            return "Hello";
+        }
+
+        @GET
+        @Path("fail")
+        public Response fail() {
+            // should return false on first (regular) connect and true on reconnect
+            schedulerFound = clientSchedulerThreadPresent();
+            cdl.countDown();
+            // simulate unsuccessful connect attempt -> force reconnect (eventSource will submit a task into scheduler)
+            return Response.status(503).build();
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorTest.java
new file mode 100644
index 0000000..617b2a0
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2017, 2018 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import org.glassfish.jersey.client.ClientAsyncExecutor;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.spi.ExecutorServiceProvider;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ClientExecutorTest extends JerseyTest {
+
+    @Path("ClientExecutorTest")
+    public static class ClientExecutorTestResource {
+
+        @GET
+        @Produces("text/plain")
+        public String get() {
+            return "get";
+        }
+    }
+
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ClientExecutorTestResource.class);
+    }
+
+    private volatile String threadName = null;
+
+    @Test
+    public void testCustomExecutorRx() throws InterruptedException {
+
+        ExecutorService clientExecutor =
+                Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("ClientExecutor-%d").build());
+
+        Client client = ClientBuilder.newBuilder().executorService(clientExecutor).build();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        testRx(client, latch);
+
+        latch.await(3, TimeUnit.SECONDS);
+
+        assertNotNull(threadName);
+        assertThat(threadName, containsString("ClientExecutor"));
+    }
+
+    @Test
+    public void testDefaultExecutorRx() throws InterruptedException {
+
+        Client client = ClientBuilder.newClient();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        testRx(client, latch);
+
+        latch.await(3, TimeUnit.SECONDS);
+        assertNotNull(threadName);
+        assertThat(threadName, containsString("jersey-client-async-executor"));
+    }
+
+    @Test
+    public void testDefaultExecutorAsync() throws InterruptedException {
+
+        Client client = ClientBuilder.newClient();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        testAsync(client, latch);
+
+        latch.await(3, TimeUnit.SECONDS);
+        assertNotNull(threadName);
+        assertThat(threadName, containsString("jersey-client-async-executor"));
+    }
+
+    @Test
+    public void testJerseyCustomExecutorAsync() throws InterruptedException {
+        Client client = ClientBuilder.newClient();
+        client.register(MyExecutorProvider.class);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        testAsync(client, latch);
+
+        latch.await(3, TimeUnit.SECONDS);
+        assertNotNull(threadName);
+        assertThat(threadName, containsString("MyExecutorProvider"));
+    }
+
+
+    private void testRx(Client client, CountDownLatch latch) {
+        client.target(UriBuilder.fromUri(getBaseUri()).path("ClientExecutorTest"))
+              .register(new MessageBodyReader<ClientExecutorTest>() {
+                  @Override
+                  public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+                      return true;
+                  }
+
+                  @Override
+                  public ClientExecutorTest readFrom(Class<ClientExecutorTest> type, Type genericType,
+                                                     Annotation[] annotations,
+                                                     MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                                                     InputStream entityStream) throws IOException, WebApplicationException {
+
+                      ClientExecutorTest.this.threadName = Thread.currentThread().getName();
+                      latch.countDown();
+
+                      return new ClientExecutorTest();
+
+                  }
+              })
+              .request()
+              .rx()
+              .get(ClientExecutorTest.class);
+    }
+
+    private void testAsync(Client client, CountDownLatch latch) {
+        client.target(UriBuilder.fromUri(getBaseUri()).path("ClientExecutorTest"))
+              .register(new MessageBodyReader<ClientExecutorTest>() {
+                  @Override
+                  public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+                      return true;
+                  }
+
+                  @Override
+                  public ClientExecutorTest readFrom(Class<ClientExecutorTest> type, Type genericType,
+                                                     Annotation[] annotations,
+                                                     MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                                                     InputStream entityStream) throws IOException, WebApplicationException {
+
+                      ClientExecutorTest.this.threadName = Thread.currentThread().getName();
+                      latch.countDown();
+
+                      return new ClientExecutorTest();
+
+                  }
+              })
+              .request()
+              .async()
+              .get(ClientExecutorTest.class);
+    }
+
+    @ClientAsyncExecutor
+    public static class MyExecutorProvider implements ExecutorServiceProvider {
+
+        public final ExecutorService executorService =
+                Executors
+                        .newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("MyExecutorProvider-%d").build());
+
+        @Override
+        public ExecutorService getExecutorService() {
+            return executorService;
+        }
+
+        @Override
+        public void dispose(ExecutorService executorService) {
+            executorService.shutdown();
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientFilterTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientFilterTest.java
new file mode 100644
index 0000000..af4290b
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientFilterTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.ResponseProcessingException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ClientFilterTest extends JerseyTest {
+
+    @Path("/test")
+    public static class FooResource {
+
+        @GET
+        public String get(@Context HttpHeaders headers) {
+            return "GET "
+                    + headers.getHeaderString(HttpHeaders.COOKIE)
+                    + headers.getHeaderString(HttpHeaders.CACHE_CONTROL);
+        }
+    }
+
+    public static class CustomRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext clientRequestContext) throws IOException {
+            clientRequestContext.abortWith(Response.ok("OK!", MediaType.TEXT_PLAIN_TYPE).build());
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(FooResource.class);
+    }
+
+    @Test
+    public void filterAbortTest() {
+        final WebTarget target = target();
+        target.register(CustomRequestFilter.class);
+        final String entity = target.request().get(String.class);
+
+        assertEquals("OK!", entity);
+    }
+
+    public static class HeaderProvidersRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext clientRequestContext) throws IOException {
+            final CacheControl cacheControl = new CacheControl();
+            cacheControl.setPrivate(true);
+            clientRequestContext.getHeaders().putSingle(HttpHeaders.CACHE_CONTROL, cacheControl);
+            clientRequestContext.getHeaders().putSingle(HttpHeaders.COOKIE, new Cookie("cookie", "cookie-value"));
+        }
+    }
+
+    @Test
+    public void filterHeaderProvidersTest() {
+        final WebTarget target = target();
+        target.register(HeaderProvidersRequestFilter.class).register(LoggingFeature.class);
+        final Response response = target.path("test").request().get(Response.class);
+        final String entity = response.readEntity(String.class);
+
+        assertTrue(entity.contains("GET"));
+        assertTrue(entity.contains("private"));
+        assertTrue(entity.contains("cookie-value"));
+    }
+
+    public static class IOExceptionResponseFilter implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws
+                IOException {
+            throw new IOException(IOExceptionResponseFilter.class.getName());
+
+        }
+    }
+
+    @Test
+    public void ioExceptionResponseFilterTest() {
+        final WebTarget target = target();
+        target.register(IOExceptionResponseFilter.class).register(LoggingFeature.class);
+
+        boolean caught = false;
+
+        try {
+            target.path("test").request().get(Response.class);
+        } catch (ResponseProcessingException e) {
+            caught = true;
+            assertNotNull(e.getCause());
+            assertEquals(IOException.class, e.getCause().getClass());
+            assertEquals(IOExceptionResponseFilter.class.getName(), e.getCause().getMessage());
+        }
+
+        assertTrue(caught);
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPathTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPathTest.java
new file mode 100644
index 0000000..ee07bd7
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPathTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test definition of path in client invocation.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class ClientPathTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class, TestResourceWithPathParams.class);
+    }
+
+    /**
+     * Test that {@link PathParam path parameters} can be passed to {@link Client#target(String)} method.
+     */
+    @Test
+    public void pathParamInTargetTest() {
+
+        Response response = client().target("http://localhost:" + getPort() + "/test/{beginBy}")
+                .resolveTemplate("beginBy", "abc")
+                .request(MediaType.TEXT_PLAIN_TYPE).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("test-get,abc", response.readEntity(String.class));
+    }
+
+    /**
+     * Tests path concatenation. (regression test for JERSEY-1114)
+     */
+    @Test
+    public void pathConcatenationTest1() {
+        Response response = client().target("http://localhost:" + getPort()).path("path").request(MediaType.TEXT_PLAIN_TYPE)
+                .get();
+        assertEquals(200, response.getStatus());
+        assertEquals("test-path", response.readEntity(String.class));
+    }
+
+    /**
+     * Tests path concatenation. (regression test for JERSEY-1114)
+     */
+    @Test
+    public void pathConcatenationTest2() {
+        Response response = client().target("http://localhost:" + getPort()).path("/path").request(MediaType.TEXT_PLAIN_TYPE)
+                .get();
+        assertEquals(200, response.getStatus());
+        assertEquals("test-path", response.readEntity(String.class));
+    }
+
+    /**
+     * Tests path concatenation. (regression test for JERSEY-1114)
+     */
+    @Test
+    public void pathConcatenationTest3() {
+        Response response = client().target("http://localhost:" + getPort()).path("/path/").path("/another/")
+                .request(MediaType.TEXT_PLAIN_TYPE).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("test-another-path", response.readEntity(String.class));
+    }
+
+    /**
+     * Tests path concatenation. (regression test for JERSEY-1114)
+     */
+    @Test
+    public void pathConcatenationTest4() {
+        Response response = client().target("http://localhost:" + getPort()).path("/path").path("another/")
+                .request(MediaType.TEXT_PLAIN_TYPE).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("test-another-path", response.readEntity(String.class));
+    }
+
+    /**
+     * Tests path concatenation. (regression test for JERSEY-1114)
+     */
+    @Test
+    public void pathConcatenationTest6() {
+        Response response = client().target("http://localhost:" + getPort() + "/").path("/path/another")
+                .request(MediaType.TEXT_PLAIN_TYPE).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("test-another-path", response.readEntity(String.class));
+    }
+
+    /**
+     * Test resource class with {@link PathParam path parameters).
+     *
+     */
+    @Path("test/{beginBy}")
+    public static class TestResourceWithPathParams {
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Consumes(MediaType.TEXT_PLAIN)
+        public String get(@PathParam(value = "beginBy") String param) {
+            return "test-get," + param;
+        }
+
+    }
+
+    /**
+     * Test resource class.
+     *
+     */
+    @Path("path")
+    public static class TestResource {
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Consumes(MediaType.TEXT_PLAIN)
+        public String get() {
+            return "test-path";
+        }
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Consumes(MediaType.TEXT_PLAIN)
+        @Path("another")
+        public String getAnother() {
+            return "test-another-path";
+        }
+
+    }
+
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPreInitTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPreInitTest.java
new file mode 100644
index 0000000..ad4d687
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPreInitTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.JerseyWebTarget;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test pre initialization of the client.
+ *
+ * @author Miroslav Fuksa
+ */
+public class ClientPreInitTest extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Path("resource")
+    public static class Resource {
+        @GET
+        public String get(@HeaderParam("filter-request") String header) {
+            return "resource:" + (header == null ? "<null>" : header);
+        }
+
+        @GET
+        @Path("child")
+        public String getChild(@HeaderParam("filter-request") String header) {
+            return "child:" + (header == null ? "<null>" : header);
+        }
+    }
+
+    public static class MyRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.getHeaders().add("filter-request", "called");
+        }
+    }
+
+    public static class MyResponseFilter implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+            responseContext.getHeaders().add("filter-response", "called");
+        }
+    }
+
+    public static class TestReader implements MessageBodyReader<Integer> {
+        public static boolean initialized;
+
+        public TestReader() {
+            TestReader.initialized = true;
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return false;
+        }
+
+        @Override
+        public Integer readFrom(Class<Integer> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                                MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            return null;
+        }
+    }
+
+    @Before
+    public void before() {
+        TestReader.initialized = false;
+    }
+
+    @Test
+    public void testNonInitialized() {
+        Client client = ClientBuilder.newClient();
+        client.register(MyResponseFilter.class);
+        client.register(TestReader.class);
+        final WebTarget target = client.target(super.getBaseUri()).path("resource");
+        assertFalse(TestReader.initialized);
+        final Response resourceResponse = target.request().get();
+        checkResponse(resourceResponse, "resource:<null>");
+        assertTrue(TestReader.initialized);
+    }
+
+    @Test
+    public void testSimplePreinitialize() {
+        Client client = ClientBuilder.newClient();
+        final WebTarget target = client.target(super.getBaseUri()).path("resource");
+        target.register(MyResponseFilter.class);
+        final WebTarget childTarget = target.path("child");
+        ((JerseyWebTarget) childTarget).preInitialize();
+        final Response response = childTarget.request().get();
+        checkResponse(response, "child:<null>");
+
+        final Response resourceResponse = target.request().get();
+        checkResponse(resourceResponse, "resource:<null>");
+    }
+
+    @Test
+    public void testReusingPreinitializedConfig() {
+        Client client = ClientBuilder.newClient();
+        client.register(TestReader.class);
+        final WebTarget target = client.target(super.getBaseUri()).path("resource");
+        target.register(MyResponseFilter.class);
+        ((JerseyWebTarget) target).preInitialize();
+        assertTrue(TestReader.initialized);
+        final WebTarget childTarget = target.path("child");
+        final Response response = childTarget.request().get();
+
+        checkResponse(response, "child:<null>");
+
+        final Response resourceResponse = target.request().get();
+        checkResponse(resourceResponse, "resource:<null>");
+    }
+
+    @Test
+    public void testReusingPreinitializedConfig2() {
+        Client client = ClientBuilder.newClient();
+        client.register(TestReader.class);
+        client.register(MyResponseFilter.class);
+
+        assertFalse(TestReader.initialized);
+        ((JerseyClient) client).preInitialize();
+        assertTrue(TestReader.initialized);
+
+        final WebTarget target = client.target(super.getBaseUri()).path("resource");
+        final WebTarget childTarget = target.path("child");
+        final Response response = childTarget.request().get();
+        checkResponse(response, "child:<null>");
+
+        final Response resourceResponse = target.request().get();
+        checkResponse(resourceResponse, "resource:<null>");
+    }
+
+    private void checkResponse(Response response, String entity) {
+        assertEquals("called", response.getHeaders().get("filter-response").get(0));
+        assertEquals(200, response.getStatus());
+        assertEquals(entity, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testRegisterOnPreinitialized1() {
+        Client client = ClientBuilder.newClient();
+        final WebTarget target = client.target(super.getBaseUri()).path("resource");
+        target.register(MyRequestFilter.class);
+        ((JerseyWebTarget) target).preInitialize();
+        target.register(MyResponseFilter.class);
+        final Response response = target.request().get();
+        checkResponse(response, "resource:called");
+    }
+
+    @Test
+    public void testRegisterOnPreinitialized2() {
+        Client client = ClientBuilder.newClient();
+        final WebTarget target = client.target(super.getBaseUri()).path("resource");
+        target.register(MyResponseFilter.class);
+        ((JerseyWebTarget) target).preInitialize();
+        final WebTarget child = target.path("child");
+        child.register(MyRequestFilter.class);
+
+        final Response response = target.request().get();
+        checkResponse(response, "resource:<null>");
+
+        final Response childResponse = child.request().get();
+        checkResponse(childResponse, "child:called");
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectHeadTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectHeadTest.java
new file mode 100644
index 0000000..204ca15
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectHeadTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010, 2018 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.client;
+
+import java.net.URI;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests that configuration of {@link ClientProperties#FOLLOW_REDIRECTS} works when HEAD method is used.
+ *
+ * @author Paul Sandoz
+ * @author Miroslav Fuksa
+ */
+public class FollowRedirectHeadTest extends JerseyTest {
+
+    @Path("resource")
+    public static class Resource {
+
+        @Path("redirect")
+        @GET
+        public Response redirect() {
+            return Response.status(303).location(URI.create("resource/final")).build();
+        }
+
+        @Path("final")
+        @GET
+        public Response afterRedirection() {
+            return Response.ok("final-entity").build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, LoggingFeature.class);
+    }
+
+    private WebTarget getTarget(boolean followRedirect) {
+        Client client = ClientBuilder.newClient(new ClientConfig().property(ClientProperties.FOLLOW_REDIRECTS,
+                followRedirect));
+        return client.target(getBaseUri()).path("resource/redirect");
+    }
+
+    @Test
+    public void testDontFollowRedirectHead() throws Exception {
+        Response response = getTarget(false).request().head();
+        Assert.assertEquals(303, response.getStatus());
+        Assert.assertTrue(response.getLocation().toString().endsWith("/final"));
+    }
+
+    @Test
+    public void testDontFollowRedirectGet() throws Exception {
+        Response response = getTarget(false).request().get();
+        Assert.assertEquals(303, response.getStatus());
+        Assert.assertTrue(response.getLocation().toString().endsWith("/final"));
+    }
+
+    @Test
+    public void testFollowRedirectHead() throws Exception {
+        Response response = getTarget(true).request().head();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertFalse(response.hasEntity());
+    }
+
+    @Test
+    public void testFollowRedirectGet() throws Exception {
+        Response response = getTarget(true).request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("final-entity", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectsTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectsTest.java
new file mode 100644
index 0000000..12610b1
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class FollowRedirectsTest extends JerseyTest {
+    @Path("/test")
+    public static class RedirectResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @GET
+        @Path("redirect")
+        public Response redirect() {
+            return Response.seeOther(UriBuilder.fromResource(RedirectResource.class).build()).build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(RedirectResource.class);
+    }
+
+    @Test
+    public void testDoFollow() {
+        final Response response = target("test/redirect").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("GET", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testDontFollow() {
+        final WebTarget target = target("test/redirect");
+        target.property(ClientProperties.FOLLOW_REDIRECTS, false);
+        assertEquals(303, target.request().get().getStatus());
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/GenericResponseTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/GenericResponseTest.java
new file mode 100644
index 0000000..14dadcd
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/GenericResponseTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import java.util.concurrent.ExecutionException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.SyncInvoker;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test {@link GenericType} with {@link Response}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class GenericResponseTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Path("resource")
+    public static class TestResource {
+
+        @GET
+        public String get() {
+            return "get";
+        }
+
+        @POST
+        public String post(String post) {
+            return post;
+        }
+    }
+
+    @Test
+    public void testPost() {
+        GenericType<Response> generic = new GenericType<Response>(Response.class);
+        Entity entity = Entity.entity("entity", MediaType.WILDCARD_TYPE);
+
+        WebTarget target = target("resource");
+        SyncInvoker sync = target.request();
+
+        Response response = sync.post(entity, generic);
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("entity", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testAsyncPost() throws ExecutionException, InterruptedException {
+        GenericType<Response> generic = new GenericType<Response>(Response.class);
+        Entity entity = Entity.entity("entity", MediaType.WILDCARD_TYPE);
+
+        WebTarget target = target("resource");
+        final AsyncInvoker async = target.request().async();
+
+        Response response = async.post(entity, generic).get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("entity", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGet() {
+        GenericType<Response> generic = new GenericType<Response>(Response.class);
+
+        WebTarget target = target("resource");
+        SyncInvoker sync = target.request();
+        Response response = sync.get(generic);
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("get", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testAsyncGet() throws ExecutionException, InterruptedException {
+        GenericType<Response> generic = new GenericType<Response>(Response.class);
+
+        WebTarget target = target("resource");
+        final AsyncInvoker async = target.request().async();
+        Response response = async.get(generic).get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("get", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGetGenericString() {
+        GenericType<String> generic = new GenericType<String>(String.class);
+
+        WebTarget target = target("resource");
+        SyncInvoker sync = target.request();
+        final String entity = sync.get(generic);
+        Assert.assertEquals("get", entity);
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpAuthorizationTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpAuthorizationTest.java
new file mode 100644
index 0000000..24d8114
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpAuthorizationTest.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.nio.charset.Charset;
+import java.security.Principal;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.internal.util.Base64;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature}.
+ *
+ * @author Miroslav Fuksa
+ */
+@RunWith(ConcurrentRunner.class)
+public class HttpAuthorizationTest extends JerseyTest {
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Digest {
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Basic {
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Alternating {
+    }
+
+    /**
+     * Alternates between BASIC and DIGEST (each is used for 2 requests).
+     */
+    @Alternating
+    public static class AlternatingDigestBasicFilter implements ContainerRequestFilter {
+
+        int counter = 0;
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if ((counter++ / 2) % 2 == 0) {
+                new BasicFilter().filter(requestContext);
+            } else {
+                new DigestFilter().filter(requestContext);
+            }
+        }
+    }
+
+    @Digest
+    public static class DigestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            final String authorization = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
+
+            if (authorization != null && authorization.trim().toUpperCase().startsWith("DIGEST")) {
+                final Matcher match = Pattern.compile("username=\"([^\"]+)\"").matcher(authorization);
+                if (!match.find()) {
+                    return;
+                }
+                final String username = match.group(1);
+
+                requestContext.setSecurityContext(new SecurityContext() {
+                    @Override
+                    public Principal getUserPrincipal() {
+                        return new Principal() {
+                            @Override
+                            public String getName() {
+                                return username;
+                            }
+                        };
+                    }
+
+                    @Override
+                    public boolean isUserInRole(String role) {
+                        return false;
+                    }
+
+                    @Override
+                    public boolean isSecure() {
+                        return false;
+                    }
+
+                    @Override
+                    public String getAuthenticationScheme() {
+                        return "DIGEST";
+                    }
+                });
+                return;
+            }
+            requestContext.abortWith(Response.status(401).header(HttpHeaders.WWW_AUTHENTICATE,
+                    "Digest realm=\"my-realm\", domain=\"\", nonce=\"n9iv3MeSNkEfM3uJt2gnBUaWUbKAljxp\", algorithm=MD5, "
+                            + "qop=\"auth\", stale=false")
+                    .build());
+        }
+    }
+
+    /**
+     * Basic Auth: password must be the same as user name except first letter is capitalized.
+     * Example: username "homer" ->  password "Homer"
+     */
+    @Basic
+    public static class BasicFilter implements ContainerRequestFilter {
+
+        static final Charset CHARACTER_SET = Charset.forName("iso-8859-1");
+        public static final String AUTH_SCHEME_CASE = "Auth-Scheme-Case";
+
+        @Override
+        public void filter(ContainerRequestContext request) throws IOException {
+
+            String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+            if (authHeader != null && authHeader.trim().toUpperCase().startsWith("BASIC")) {
+                String decoded = new String(Base64.decode(authHeader.substring(6).getBytes()), CHARACTER_SET);
+                //                String decoded = Base64.decodeAsString(authHeader.substring(6));
+                final String[] split = decoded.split(":");
+                final String username = split[0];
+                final String pwd = split[1];
+                String capitalizedUserName = username.substring(0, 1).toUpperCase() + username.substring(1);
+                if (capitalizedUserName.equals(pwd)) {
+                    request.setSecurityContext(new SecurityContext() {
+                        @Override
+                        public Principal getUserPrincipal() {
+                            return new Principal() {
+                                @Override
+                                public String getName() {
+                                    return username;
+                                }
+                            };
+                        }
+
+                        @Override
+                        public boolean isUserInRole(String role) {
+                            return true;
+                        }
+
+                        @Override
+                        public boolean isSecure() {
+                            return false;
+                        }
+
+                        @Override
+                        public String getAuthenticationScheme() {
+                            return "BASIC";
+                        }
+                    });
+                    return;
+                }
+            }
+            final String authSchemeCase = request.getHeaderString(AUTH_SCHEME_CASE);
+
+            final String authScheme;
+            if ("uppercase".equals(authSchemeCase)) {
+                authScheme = "BASIC";
+            } else if ("lowercase".equals(authSchemeCase)) {
+                authScheme = "basic";
+            } else {
+                authScheme = "Basic";
+            }
+
+            request.abortWith(Response.status(401).header(HttpHeaders.WWW_AUTHENTICATE, authScheme).build());
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(MyResource.class);
+        resourceConfig.register(LoggingFeature.class);
+        resourceConfig.register(new BasicFilter());
+        resourceConfig.register(new DigestFilter());
+        resourceConfig.register(new AlternatingDigestBasicFilter());
+        return resourceConfig;
+    }
+
+    @Path("resource")
+    public static class MyResource {
+
+        @Context
+        SecurityContext securityContext;
+
+        @GET
+        public String unsecure() {
+            return "unsecure";
+        }
+
+        @GET
+        @Path("basic")
+        @Basic
+        public String basic() {
+            return securityContext.getAuthenticationScheme() + ":" + securityContext.getUserPrincipal().getName();
+        }
+
+        @GET
+        @Path("digest")
+        @Digest
+        public String digest() {
+            return securityContext.getAuthenticationScheme() + ":" + securityContext.getUserPrincipal().getName();
+        }
+
+        @GET
+        @Path("alternating")
+        @Alternating
+        public String alternating() {
+            return securityContext.getAuthenticationScheme() + ":" + securityContext.getUserPrincipal().getName();
+        }
+
+    }
+
+    @Test
+    public void testBasicPreemptive() {
+        Response response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.basicBuilder().credentials("homer", "Homer").build())
+                .request().get();
+        check(response, 200, "BASIC:homer");
+    }
+
+    @Test
+    public void testBasicNonPreemptive() {
+        Response response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.basicBuilder().nonPreemptive().credentials("homer", "Homer").build())
+                .request().get();
+        check(response, 200, "BASIC:homer");
+    }
+
+    @Test
+    public void testBasicNonPreemptiveWithEmptyPassword() {
+        final WebTarget target = target().path("resource")
+                .register(HttpAuthenticationFeature.basicBuilder().nonPreemptive().build());
+        Response response = target.request().get();
+        check(response, 200, "unsecure");
+
+        try {
+            response = target().path("resource").path("basic")
+                    .register(HttpAuthenticationFeature.basicBuilder().nonPreemptive().build())
+                    .request().get();
+            Assert.fail("should throw an exception as credentials are missing");
+        } catch (Exception e) {
+            // ok
+        }
+
+        response = target.path("basic").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_PASSWORD, "Bart")
+                .get();
+
+        check(response, 200, "BASIC:bart");
+    }
+
+    @Test
+    public void testUniversalBasic() {
+        Response response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "Homer").build())
+                .request().get();
+        check(response, 200, "BASIC:homer");
+    }
+
+    /**
+     * Reproducer for JERSEY-2941: BasicAuthenticator#filterResponseAndAuthenticate: auth-scheme checks should be case
+     * insensitve.
+     */
+    @Test
+    public void testUniversalBasicCaseSensitivity() {
+        Response response;
+
+        response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "Homer").build())
+                .request()
+                // no AUTH_SCHEME_CASE header = mixed case
+                .get();
+        check(response, 200, "BASIC:homer");
+
+        response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "Homer").build())
+                .request()
+                .header(BasicFilter.AUTH_SCHEME_CASE, "lowercase")
+                .get();
+        check(response, 200, "BASIC:homer");
+
+        response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "Homer").build())
+                .request()
+                .header(BasicFilter.AUTH_SCHEME_CASE, "uppercase")
+                .get();
+        check(response, 200, "BASIC:homer");
+    }
+
+    @Test
+    public void testUniversalBasicWrongPassword() {
+        Response response = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "FOO").build())
+                .request().get();
+        check(response, 401);
+    }
+
+    @Test
+    public void testBasicWithDifferentCredentials() {
+        final WebTarget target = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.basicBuilder().credentials("marge", "Marge").build());
+
+        _testBasicWithDifferentCredentials(target);
+    }
+
+    @Test
+    public void testBasicUniversalWithDifferentCredentials() {
+        final WebTarget target = target().path("resource").path("basic")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("marge", "Marge").build());
+
+        _testBasicWithDifferentCredentials(target);
+    }
+
+    public void _testBasicWithDifferentCredentials(WebTarget target) {
+        Response response = target
+                .request().get();
+        check(response, 200, "BASIC:marge");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, "Bart")
+                .get();
+
+        check(response, 200, "BASIC:bart");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_PASSWORD, "Bart")
+                .get();
+
+        check(response, 200, "BASIC:bart");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_PASSWORD, "Bart")
+                .get();
+
+        check(response, 200, "BASIC:marge");
+    }
+
+    @Test
+    public void testDigest() {
+        Response response = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.digest("homer", "Homer"))
+                .request().get();
+        check(response, 200, "DIGEST:homer");
+    }
+
+    @Test
+    public void testDigestWithPasswords() {
+        final WebTarget target = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.digest("homer", "Homer"));
+        _testDigestWithPasswords(target);
+    }
+
+    @Test
+    public void testUniversalDigestWithPasswords() {
+        final WebTarget target = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "Homer").build());
+        _testDigestWithPasswords(target);
+    }
+
+    public void _testDigestWithPasswords(WebTarget target) {
+        Response response = target.request().get();
+        check(response, 200, "DIGEST:homer");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_PASSWORD, "Bart")
+                .get();
+        check(response, 200, "DIGEST:bart");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_PASSWORD, "Bart")
+                .get();
+        check(response, 200, "DIGEST:homer");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, "Bart")
+                .get();
+        check(response, 200, "DIGEST:bart");
+    }
+
+    @Test
+    public void testDigestWithEmptyDefaultPassword() {
+        final WebTarget target = target().path("resource")
+                .register(HttpAuthenticationFeature.digest());
+        _testDigestWithEmptyDefaultPassword(target);
+    }
+
+    @Test
+    public void testDigestUniversalWithEmptyDefaultPassword() {
+        final WebTarget target = target().path("resource")
+                .register(HttpAuthenticationFeature.universalBuilder().build());
+        _testDigestWithEmptyDefaultPassword(target);
+    }
+
+    public void _testDigestWithEmptyDefaultPassword(WebTarget target) {
+        Response response = target.request().get();
+        check(response, 200, "unsecure");
+
+        response = target.request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_PASSWORD, "Bart")
+                .get();
+        check(response, 200, "unsecure");
+
+        response = target.path("digest").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_PASSWORD, "Bart")
+                .get();
+        check(response, 200, "DIGEST:bart");
+
+        try {
+            target.path("digest").request()
+                    .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_USERNAME, "bart")
+                    .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_BASIC_PASSWORD, "Bart")
+                    .get();
+            Assert.fail("should throw an exception as no credentials were supplied for digest auth");
+        } catch (Exception e) {
+            // ok
+        }
+
+        response = target.path("digest").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, "Bart")
+                .get();
+        check(response, 200, "DIGEST:bart");
+    }
+
+    private void check(Response response, int status, String entity) {
+        Assert.assertEquals(status, response.getStatus());
+        Assert.assertEquals(entity, response.readEntity(String.class));
+    }
+
+    private void check(Response response, int status) {
+        Assert.assertEquals(status, response.getStatus());
+    }
+
+    @Test
+    public void testDigestUniversalSimple() {
+        Response response = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.universalBuilder().credentials("homer", "Homer").build())
+                .request().get();
+        check(response, 200, "DIGEST:homer");
+    }
+
+    @Test
+    public void testDigestUniversalSimple2() {
+        Response response = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.universalBuilder().credentialsForDigest("homer", "Homer").build())
+                .request().get();
+        check(response, 200, "DIGEST:homer");
+    }
+
+    @Test
+    public void testDigestUniversalSimple3() {
+        Response response = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.universalBuilder()
+                        .credentialsForDigest("homer", "Homer")
+                        .credentialsForBasic("foo", "bar")
+                        .build())
+                .request().get();
+        check(response, 200, "DIGEST:homer");
+    }
+
+    @Test
+    public void testDigestUniversalSimple4() {
+        Response response = target().path("resource").path("digest")
+                .register(HttpAuthenticationFeature.universal("homer", "Homer"))
+                .request().get();
+        check(response, 200, "DIGEST:homer");
+    }
+
+    @Test
+    public void testUniversal() {
+        final WebTarget target = target().path("resource")
+                .register(HttpAuthenticationFeature.universal("homer", "Homer"));
+
+        check(target.request().get(), 200, "unsecure");
+        check(target.path("digest").request().get(), 200, "DIGEST:homer");
+        check(target.path("basic").request().get(), 200, "BASIC:homer");
+        check(target.path("basic").request().get(), 200, "BASIC:homer");
+        check(target.path("digest").request().get(), 200, "DIGEST:homer");
+        check(target.path("digest").request().get(), 200, "DIGEST:homer");
+        check(target.path("digest").request().get(), 200, "DIGEST:homer");
+        check(target.path("basic").request().get(), 200, "BASIC:homer");
+        check(target.path("basic").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_PASSWORD, "Bart").get(), 200, "BASIC:homer");
+
+        check(target.path("digest").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_DIGEST_PASSWORD, "Bart").get(), 200, "DIGEST:bart");
+
+        check(target.path("digest").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, "Bart").get(), 200, "DIGEST:bart");
+
+        check(target.path("alternating").request().get(), 200, "BASIC:homer");
+        check(target.path("alternating").request().get(), 200, "DIGEST:homer");
+        check(target.path("alternating").request().get(), 200, "BASIC:homer");
+        check(target.path("basic").request().get(), 200, "BASIC:homer");
+        check(target.path("alternating").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, "Bart").get(), 200, "DIGEST:bart");
+        check(target.path("alternating").request().property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, "bart")
+                .property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, "Bart").get(), 200, "BASIC:bart");
+
+    }
+
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpDigestAuthFilterTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpDigestAuthFilterTest.java
new file mode 100644
index 0000000..14aaee9
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpDigestAuthFilterTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2010, 2018 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.client;
+
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.uri.UriComponent;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Stefan Katerkamp <stefan at katerkamp.de>
+ */
+public class HttpDigestAuthFilterTest extends JerseyTest {
+
+    private static final String DIGEST_TEST_LOGIN = "user";
+    private static final String DIGEST_TEST_PASS = "password";
+    private static final String DIGEST_TEST_INVALIDPASS = "nopass";
+    // Digest string expected for OK auth:
+    // Digest realm="test", nonce="eDePFNeJBAA=a874814ec55647862b66a747632603e5825acd39",
+    //   algorithm=MD5, domain="/auth-digest/", qop="auth"
+    private static final String DIGEST_TEST_NONCE = "eDePFNeJBAA=a874814ec55647862b66a747632603e5825acd39";
+    private static final String DIGEST_TEST_REALM = "test";
+    private static final String DIGEST_TEST_DOMAIN = "/auth-digest/";
+    private static int ncExpected = 1;
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Path("/auth-digest")
+    public static class Resource {
+
+        @Context
+        private HttpHeaders httpHeaders;
+        @Context
+        private UriInfo uriInfo;
+
+        @GET
+        public Response get1() {
+            return verify();
+        }
+
+
+        @GET
+        @Path("ěščřžýáíé")
+        public Response getEncoding() {
+            return verify();
+        }
+
+        private Response verify() {
+            if (httpHeaders.getRequestHeader(HttpHeaders.AUTHORIZATION) == null) {
+                // the first request has no authorization header, tell filter its 401
+                // and send filter back seed for the new to be built header
+                ResponseBuilder responseBuilder = Response.status(Response.Status.UNAUTHORIZED);
+                responseBuilder = responseBuilder.header(HttpHeaders.WWW_AUTHENTICATE,
+                        "Digest realm=\"" + DIGEST_TEST_REALM + "\", "
+                                + "nonce=\"" + DIGEST_TEST_NONCE + "\", "
+                                + "algorithm=MD5, "
+                                + "domain=\"" + DIGEST_TEST_DOMAIN + "\", qop=\"auth\"");
+                return responseBuilder.build();
+            } else {
+                // the filter takes the seed and adds the header
+                final List<String> authList = httpHeaders.getRequestHeader(HttpHeaders.AUTHORIZATION);
+                if (authList.size() != 1) {
+                    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+                }
+                final String authHeader = authList.get(0);
+
+                final String ha1 = md5(DIGEST_TEST_LOGIN, DIGEST_TEST_REALM, DIGEST_TEST_PASS);
+                final String requestUri = UriComponent.fullRelativeUri(uriInfo.getRequestUri());
+                final String ha2 = md5("GET", requestUri.startsWith("/") ? requestUri : "/" + requestUri);
+                final String response = md5(
+                        ha1,
+                        DIGEST_TEST_NONCE,
+                        getDigestAuthHeaderValue(authHeader, "nc="),
+                        getDigestAuthHeaderValue(authHeader, "cnonce="),
+                        getDigestAuthHeaderValue(authHeader, "qop="),
+                        ha2);
+
+                // this generates INTERNAL_SERVER_ERROR if not matching
+                Assert.assertEquals(ncExpected, Integer.parseInt(getDigestAuthHeaderValue(authHeader, "nc=")));
+
+                if (response.equals(getDigestAuthHeaderValue(authHeader, "response="))) {
+                    return Response.ok().build();
+                } else {
+                    return Response.status(Response.Status.UNAUTHORIZED).build();
+                }
+            }
+        }
+
+        private static final Charset CHARACTER_SET = Charset.forName("iso-8859-1");
+
+        /**
+         * Colon separated value MD5 hash. Call md5 method of the filter.
+         *
+         * @param tokens one or more strings
+         * @return M5 hash string
+         */
+        static String md5(final String... tokens) {
+            final StringBuilder sb = new StringBuilder(100);
+            for (final String token : tokens) {
+                if (sb.length() > 0) {
+                    sb.append(':');
+                }
+                sb.append(token);
+            }
+
+            final MessageDigest md;
+            try {
+                md = MessageDigest.getInstance("MD5");
+            } catch (final NoSuchAlgorithmException ex) {
+                throw new ProcessingException(ex.getMessage());
+            }
+            md.update(sb.toString().getBytes(CHARACTER_SET), 0, sb.length());
+            final byte[] md5hash = md.digest();
+            return bytesToHex(md5hash);
+        }
+
+        /**
+         * Convert bytes array to hex string.
+         *
+         * @param bytes array of bytes
+         * @return hex string
+         */
+        private static String bytesToHex(final byte[] bytes) {
+            final char[] hexChars = new char[bytes.length * 2];
+            int v;
+            for (int j = 0; j < bytes.length; j++) {
+                v = bytes[j] & 0xFF;
+                hexChars[j * 2] = HEX_ARRAY[v >>> 4];
+                hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
+            }
+            return new String(hexChars);
+        }
+
+        private static final char[] HEX_ARRAY = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+        /**
+         * Get a value of the Digest Auth Header.
+         *
+         * @param authHeader digest auth header string
+         * @param keyName key of the value to retrieve
+         * @return value string
+         */
+        static String getDigestAuthHeaderValue(final String authHeader, final String keyName) {
+            final int i1 = authHeader.indexOf(keyName);
+
+            if (i1 == -1) {
+                return null;
+            }
+
+            String value = authHeader.substring(
+                    authHeader.indexOf('=', i1) + 1,
+                    (authHeader.indexOf(',', i1) != -1
+                            ? authHeader.indexOf(',', i1) : authHeader.length()));
+
+            value = value.trim();
+            if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') {
+                value = value.substring(1, value.length() - 1);
+            }
+
+            return value;
+        }
+    }
+
+    @Test
+    public void testHttpDigestAuthFilter() {
+        testRequest("auth-digest");
+    }
+
+    @Test
+    public void testHttpDigestAuthFilterWithEncodedUri() {
+        testRequest("auth-digest/ěščřžýáíé");
+    }
+
+    @Test
+    public void testHttpDigestAuthFilterWithParams() {
+        testRequest("auth-digest", true);
+    }
+
+    @Test
+    public void testHttpDigestAuthFilterWithEncodedUriAndParams() {
+        testRequest("auth-digest/ěščřžýáíé", true);
+    }
+
+    private void testRequest(final String path) {
+        testRequest(path, false);
+    }
+
+    private void testRequest(final String path, final boolean addParams) {
+        WebTarget resource = target()
+                .register(HttpAuthenticationFeature.digest(DIGEST_TEST_LOGIN, DIGEST_TEST_PASS))
+                .path(path);
+
+        if (addParams) {
+            resource = resource.matrixParam("bar", "foo").queryParam("foo", "bar");
+        }
+
+        ncExpected = 1;
+        final Response r1 = resource.request().get();
+        Assert.assertEquals(Response.Status.fromStatusCode(r1.getStatus()), Response.Status.OK);
+    }
+
+
+    @Test
+    public void testPreemptive() {
+        final WebTarget resource = target()
+                .register(HttpAuthenticationFeature.digest(DIGEST_TEST_LOGIN, DIGEST_TEST_PASS))
+                .path("auth-digest");
+
+        ncExpected = 1;
+        final Response r1 = resource.request().get();
+        Assert.assertEquals(Response.Status.fromStatusCode(r1.getStatus()), Response.Status.OK);
+
+        ncExpected = 2;
+        final Response r2 = resource.request().get();
+        Assert.assertEquals(Response.Status.fromStatusCode(r2.getStatus()), Response.Status.OK);
+
+        ncExpected = 3;
+        final Response r3 = resource.request().get();
+        Assert.assertEquals(Response.Status.fromStatusCode(r3.getStatus()), Response.Status.OK);
+
+    }
+
+    @Test
+    public void testAuthentication() {
+        final WebTarget resource = target()
+                .register(HttpAuthenticationFeature.digest(DIGEST_TEST_LOGIN, DIGEST_TEST_INVALIDPASS))
+                .path("auth-digest");
+
+        ncExpected = 1;
+        final Response r1 = resource.request().get();
+        Assert.assertEquals(Response.Status.fromStatusCode(r1.getStatus()), Response.Status.UNAUTHORIZED);
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java
new file mode 100644
index 0000000..546fd54
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.util.concurrent.Future;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests HTTP methods and entity presence.
+ *
+ * @author Miroslav Fuksa
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class HttpMethodEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testGet() {
+        _test("GET", true, true);
+        _test("GET", false, false);
+    }
+
+    @Test
+    public void testPost() {
+        _test("POST", true, false);
+        _test("POST", false, false);
+    }
+
+    @Test
+    public void testPut() {
+        _test("PUT", true, false);
+        _test("PUT", false, true);
+    }
+
+    @Test
+    public void testDelete() {
+        _test("DELETE", true, true);
+        _test("DELETE", false, false);
+    }
+
+    @Test
+    public void testHead() {
+        _test("HEAD", true, true);
+        _test("HEAD", false, false);
+    }
+
+    @Test
+    public void testOptions() {
+        _test("OPTIONS", true, true);
+        _test("OPTIONS", false, false);
+    }
+
+    /**
+     * Reproducer for JERSEY-2370: Sending POST without body.
+     */
+    @Test
+    public void testEmptyPostWithoutContentType() {
+        final WebTarget resource = target().path("resource");
+        try {
+            final Future<Response> future = resource.request().async().post(null);
+            assertEquals(200, future.get().getStatus());
+
+            final Response response = resource.request().post(null);
+            assertEquals(200, response.getStatus());
+        } catch (Exception e) {
+            fail("Sending POST method without entity should not fail.");
+        }
+    }
+
+    /**
+     * Reproducer for JERSEY-2370: Sending POST without body.
+     */
+    @Test
+    public void testEmptyPostWithContentType() {
+        final WebTarget resource = target().path("resource");
+        try {
+            final Future<Response> future = resource.request().async().post(Entity.entity(null, "text/plain"));
+            assertEquals(200, future.get().getStatus());
+
+            final Response response = resource.request().post(Entity.entity(null, "text/plain"));
+            assertEquals(200, response.getStatus());
+        } catch (Exception e) {
+            fail("Sending POST method without entity should not fail.");
+        }
+    }
+
+    public void _test(String method, boolean entityPresent, boolean shouldFail) {
+        Entity entity = entityPresent ? Entity.entity("entity", MediaType.TEXT_PLAIN_TYPE) : null;
+        _testSync(method, entity, shouldFail);
+        _testAsync(method, entity, shouldFail);
+    }
+
+    public void _testAsync(String method, Entity entity, boolean shouldFail) {
+        try {
+            final Future<Response> future = target().path("resource").request().async().method(method, entity);
+            if (shouldFail) {
+                fail("The method should fail.");
+            }
+            assertEquals(200, future.get().getStatus());
+        } catch (Exception e) {
+            if (!shouldFail) {
+                fail("The method " + method + " with entity=" + (entity != null) + " should not fail.");
+            }
+        }
+    }
+
+    public void _testSync(String method, Entity entity, boolean shouldFail) {
+        try {
+            final Response response = target().path("resource").request().method(method, entity);
+            assertEquals(200, response.getStatus());
+            if (shouldFail) {
+                fail("The method should fail.");
+            }
+        } catch (Exception e) {
+            if (!shouldFail) {
+                fail("The method " + method + " with entityPresent=" + (entity != null) + " should not fail.");
+            }
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @Context
+        HttpHeaders httpHeaders;
+
+        @GET
+        public String get() {
+            return "get";
+        }
+
+        @POST
+        public String post(String str) {
+            // See JERSEY-1455
+            assertFalse(httpHeaders.getRequestHeaders().containsKey(HttpHeaders.CONTENT_ENCODING));
+            assertFalse(httpHeaders.getRequestHeaders().containsKey(HttpHeaders.CONTENT_LANGUAGE));
+
+            return "post";
+        }
+
+        @PUT
+        public String put(String str) {
+            return "put";
+        }
+
+        @HEAD
+        public String head() {
+            return "head";
+        }
+
+        @DELETE
+        public String delete() {
+            return "delete";
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectedClientBodyWorker.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectedClientBodyWorker.java
new file mode 100644
index 0000000..37e544c
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectedClientBodyWorker.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test if JAX-RS injection points work in client side providers.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InjectedClientBodyWorker extends JerseyTest {
+
+    // media types are used to determine what kind of injection should be tested
+    static final String ProviderType = "test/providers";
+    static final String ConfigurationTYPE = "test/configuration";
+
+    public static class MyContext {}
+
+    @Provider
+    public static class MyContextResolver implements ContextResolver<MyContext> {
+
+        @Override
+        public MyContext getContext(Class<?> type) {
+            return null;
+        }
+    }
+
+    @Provider
+    @Produces(ProviderType)
+    public static class ProvidersInjectedWriter implements MessageBodyWriter<String> {
+
+        @Context
+        Providers providers;
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(String t, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+
+            // make sure no exception occurs here
+            providers.getExceptionMapper(Exception.class);
+
+            final ContextResolver<MyContext> contextResolver = providers
+                    .getContextResolver(MyContext.class, MediaType.valueOf(ProviderType));
+            entityStream.write(String.format("%s", contextResolver).getBytes());
+        }
+    }
+
+    @Provider
+    @Consumes(ProviderType)
+    public static class ProvidersInjectedReader implements MessageBodyReader<String> {
+
+        @Context
+        Providers providers;
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public String readFrom(Class<String> type, Type genericType, Annotation[] annotations,
+                               MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                               InputStream entityStream) throws IOException, WebApplicationException {
+
+            // make sure no exception occurs here
+            providers.getExceptionMapper(Exception.class);
+
+            final ContextResolver<MyContext> contextResolver = providers
+                    .getContextResolver(MyContext.class, MediaType.valueOf(ProviderType));
+            return String.format("%s", contextResolver);
+        }
+
+    }
+
+    @Provider
+    @Produces(ConfigurationTYPE)
+    public static class ConfigurationInjectedWriter implements MessageBodyWriter<String> {
+
+        @Context
+        Configuration configuration;
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(String t, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+
+            final boolean ctxResolverRegistered = configuration.isRegistered(MyContextResolver.class);
+            entityStream.write(String.format("%b", ctxResolverRegistered).getBytes());
+        }
+    }
+
+    @Provider
+    @Consumes(ConfigurationTYPE)
+    public static class ConfigurationInjectedReader implements MessageBodyReader<String> {
+
+        @Context
+        Configuration configuration;
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public String readFrom(Class<String> type, Type genericType, Annotation[] annotations,
+                               MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                               InputStream entityStream) throws IOException, WebApplicationException {
+            final boolean ctxResolverRegistered = configuration.isRegistered(MyContextResolver.class);
+            return String.format("%b", ctxResolverRegistered);
+        }
+    }
+
+    @Path("echo")
+    public static class EchoResource {
+
+        @POST
+        @Consumes({ProviderType, ConfigurationTYPE, MediaType.TEXT_PLAIN})
+        @Produces({ProviderType, ConfigurationTYPE, MediaType.TEXT_PLAIN})
+        public String post(String p) {
+            return p;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(EchoResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config
+                .register(ProvidersInjectedWriter.class)
+                .register(ConfigurationInjectedWriter.class)
+                .register(ProvidersInjectedReader.class)
+                .register(ConfigurationInjectedReader.class);
+    }
+
+    @Test
+    public void testProvidersInReader() throws Exception {
+        _testProviders(ProviderType, MediaType.TEXT_PLAIN);
+    }
+
+    @Test
+    public void testProvidersInWriter() throws Exception {
+        _testProviders(MediaType.TEXT_PLAIN, ProviderType);
+    }
+
+    @Test
+    public void testConfigurationInReader() throws Exception {
+        testConfiguration(ConfigurationTYPE, MediaType.TEXT_PLAIN);
+    }
+
+    @Test
+    public void testConfigurationInWriter() throws Exception {
+        testConfiguration(MediaType.TEXT_PLAIN, ConfigurationTYPE);
+    }
+
+    private void _testProviders(final String incomingType, final String outgoingType) throws Exception {
+
+        final String postWithoutProviderResult = target("echo")
+                .request(outgoingType)
+                .post(Entity.entity("does not matter", incomingType), String.class);
+        assertThat(postWithoutProviderResult, is("null"));
+
+        final String postWithProviderResult = target("echo")
+                .register(MyContextResolver.class)
+                .request(outgoingType)
+                .post(Entity.entity("ignored", incomingType), String.class);
+        assertThat(postWithProviderResult, containsString(MyContextResolver.class.getName()));
+    }
+
+    private void testConfiguration(final String incomingType, final String outgoingType) throws Exception {
+
+        final String postWithoutProviderResult = target("echo")
+                .request(incomingType)
+                .post(Entity.entity("whatever", outgoingType), String.class);
+        assertThat(postWithoutProviderResult, is("false"));
+
+        final String postWithProviderResult = target("echo")
+                .register(MyContextResolver.class)
+                .request(incomingType)
+                .post(Entity.entity("bummer", outgoingType), String.class);
+        assertThat(postWithProviderResult, is("true"));
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectionManagerProviderTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectionManagerProviderTest.java
new file mode 100644
index 0000000..6ee393e
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectionManagerProviderTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2014, 2018 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.client;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.InjectionManagerProvider;
+import org.glassfish.jersey.client.InjectionManagerClientProvider;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link InjectionManagerClientProvider}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class InjectionManagerProviderTest extends JerseyTest {
+
+    @Path("resource")
+    public static class TestResource {
+        @POST
+        public String echo(String entity) {
+            return entity;
+        }
+    }
+
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+
+    public static class MyInjectedService {
+        public final String name;
+
+        public MyInjectedService(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    public static class BinderFeature implements Feature {
+
+        public final String name;
+
+        public BinderFeature(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public boolean configure(FeatureContext context) {
+            context.register(new AbstractBinder() {
+                @Override
+                protected void configure() {
+                    bind(new MyInjectedService(name)).to(MyInjectedService.class);
+                }
+            });
+            return true;
+        }
+    }
+
+
+    public static class MyRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            final InjectionManager injectionManager = InjectionManagerClientProvider.getInjectionManager(requestContext);
+            final MyInjectedService service = injectionManager.getInstance(MyInjectedService.class);
+            final String name = service.getName();
+            requestContext.setEntity(name);
+        }
+    }
+
+
+    @Test
+    public void testRequestFilterInstance() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("hello"))
+                .register(new MyRequestFilter())
+                .request().post(Entity.entity("will-be-overwritten", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("hello", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testRequestFilterClass() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("hello"))
+                .register(MyRequestFilter.class)
+                .request().post(Entity.entity("will-be-overwritten", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("hello", response.readEntity(String.class));
+    }
+
+
+    public static class MyResponseFilter implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+
+            final InjectionManager locator = InjectionManagerClientProvider.getInjectionManager(responseContext);
+            final MyInjectedService service = locator.getInstance(MyInjectedService.class);
+            final String name = service.getName();
+            responseContext.setEntityStream(new ByteArrayInputStream(name.getBytes()));
+        }
+    }
+
+    @Test
+    public void testResponseFilterInstance() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("world"))
+                .register(new MyResponseFilter())
+                .request().post(Entity.entity("will-be-overwritten", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("world", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResponseFilterClass() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("world"))
+                .register(MyResponseFilter.class)
+                .request().post(Entity.entity("will-be-overwritten", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("world", response.readEntity(String.class));
+    }
+
+    public static class MyFeature implements Feature {
+
+        @Override
+        public boolean configure(FeatureContext context) {
+            context.register(MyFeatureInterceptor.class);
+            return true;
+        }
+
+        public static class MyFeatureInterceptor implements WriterInterceptor {
+            private final String name;
+
+            @Inject
+            public MyFeatureInterceptor(MyInjectedService injectedService) {
+                this.name = injectedService.getName();
+            }
+
+            @Override
+            public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+                context.setEntity(context.getEntity() + "-interceptor-" + name);
+                context.proceed();
+            }
+        }
+    }
+
+    @Test
+    public void testFeatureInstance() {
+        final Response response = target().path("resource")
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(new MyInjectedService("feature")).to(MyInjectedService.class);
+                    }
+                })
+                .register(new MyFeature())
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-interceptor-feature", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testFeatureClass() {
+        final Response response = target().path("resource")
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(new MyInjectedService("feature")).to(MyInjectedService.class);
+                    }
+                })
+                .register(MyFeature.class)
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-interceptor-feature", response.readEntity(String.class));
+    }
+
+
+    public static class MyWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final InjectionManager serviceLocator = InjectionManagerProvider.getInjectionManager(context);
+            final MyInjectedService service = serviceLocator.getInstance(MyInjectedService.class);
+            context.setEntity(((String) context.getEntity()) + "-writer-interceptor-" + service.getName());
+            context.proceed();
+        }
+    }
+
+    @Test
+    public void testWriterInterceptorInstance() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("universe"))
+                .register(new MyWriterInterceptor())
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-writer-interceptor-universe", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testWriterInterceptorClass() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("universe"))
+                .register(MyWriterInterceptor.class)
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-writer-interceptor-universe", response.readEntity(String.class));
+    }
+
+
+    public static class MyReaderInterceptor implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final Object entity = context.proceed();
+            if (!(entity instanceof String)) {
+                return entity;
+            }
+            final String stringEntity = (String) entity;
+            final InjectionManager serviceLocator = InjectionManagerProvider.getInjectionManager(context);
+            final MyInjectedService service = serviceLocator.getInstance(MyInjectedService.class);
+            return stringEntity + "-reader-interceptor-" + service.getName();
+        }
+    }
+
+
+    @Test
+    public void testReaderInterceptorInstance() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("universe"))
+                .register(new MyReaderInterceptor())
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-reader-interceptor-universe", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testReaderInterceptorClass() {
+        final Response response = target().path("resource")
+                .register(new BinderFeature("universe"))
+                .register(MyReaderInterceptor.class)
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-reader-interceptor-universe", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/JaxRsTimeoutTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/JaxRsTimeoutTest.java
new file mode 100644
index 0000000..63df9a2
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/JaxRsTimeoutTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017, 2018 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.client;
+
+import java.net.SocketTimeoutException;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.JerseyWebTarget;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class JaxRsTimeoutTest extends JerseyTest {
+
+    private final Client client =
+            ClientBuilder.newBuilder().readTimeout(2000, TimeUnit.MILLISECONDS).build();
+
+    @Path("/test")
+    public static class TimeoutResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @GET
+        @Path("timeout")
+        public String getTimeout() {
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            return "GET";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TimeoutTest.TimeoutResource.class);
+    }
+
+    @Override
+    protected Client getClient() {
+        return client;
+    }
+
+    @Test
+    public void testFast() {
+        Response r = target("test").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("GET", r.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlow() {
+        try {
+            target("test/timeout").request().get();
+            fail("Timeout expected.");
+        } catch (ProcessingException e) {
+            if (!(e.getCause() instanceof SocketTimeoutException)) {
+                e.printStackTrace();
+                fail();
+            }
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/NonSuccessfulResponseTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/NonSuccessfulResponseTest.java
new file mode 100644
index 0000000..9075628
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/NonSuccessfulResponseTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013, 2018 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.client;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.SyncInvoker;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test no successful (3XX, 4XX, 5XX) responses with no empty body.
+ *
+ * @author Ballesi Ezequiel (ezequielballesi at gmail.com)
+ */
+public class NonSuccessfulResponseTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        // NOTE: This is not necessary when using connector, that does not follow redirects by default, such as HttpUrlConnector.
+        // However, the testcase would fail when FOLLOW_REDIRECTS is set to true. Therefor, we configure the behaviour on
+        // redirects explicitly.
+        config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+    }
+
+    @Path("resource")
+    public static class TestResource {
+
+        @GET
+        @Path("/{status}")
+        public Response getXXX(@PathParam("status") int status) {
+            return Response.status(status).entity("get").build();
+        }
+
+        @POST
+        @Path("/{status}")
+        public Response postXXX(@PathParam("status") int status, String post) {
+            return Response.status(status).entity(post).build();
+        }
+
+    }
+
+    @Test
+    public void testGet3XX() {
+        generalTestGet(302);
+    }
+
+    @Test
+    public void testPost3XX() {
+        generalTestPost(302);
+    }
+
+    @Test
+    public void testGet4XX() {
+        generalTestGet(401);
+    }
+
+    @Test
+    public void testPost4XX() {
+        generalTestPost(401);
+    }
+
+    @Test
+    public void testGet5XX() {
+        generalTestGet(500);
+    }
+
+    @Test
+    public void testPost5XX() {
+        generalTestPost(500);
+    }
+
+    private void generalTestGet(int status) {
+        WebTarget target = target("resource").path(Integer.toString(status));
+        SyncInvoker sync = target.request();
+        Response response = sync.get(Response.class);
+        Assert.assertEquals(status, response.getStatus());
+        Assert.assertEquals("get", response.readEntity(String.class));
+    }
+
+    private void generalTestPost(int status) {
+        Entity<String> entity = Entity.entity("entity", MediaType.WILDCARD_TYPE);
+        WebTarget target = target("resource").path(Integer.toString(status));
+        SyncInvoker sync = target.request();
+        Response response = sync.post(entity, Response.class);
+        Assert.assertEquals(status, response.getStatus());
+        Assert.assertEquals("entity", response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RequestScopedReadEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RequestScopedReadEntityTest.java
new file mode 100644
index 0000000..b30880d
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RequestScopedReadEntityTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * TODO: javadoc.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class RequestScopedReadEntityTest extends JerseyTest {
+
+    public static class Message {
+
+        private final String text;
+
+        public Message(String text) {
+            this.text = text;
+        }
+    }
+
+    @Path("simple")
+    public static class SimpleResource {
+
+        @GET
+        @Produces("text/plain")
+        public String getIt() {
+            return "passed";
+        }
+    }
+
+    @Produces("text/plain")
+    public static class ScopedMessageEntityProvider extends AbstractMessageReaderWriterProvider<Message> {
+
+        @Inject
+        private Provider<ClientRequest> clientRequestProvider;
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == Message.class && mediaType.equals(MediaType.TEXT_PLAIN_TYPE);
+        }
+
+        @Override
+        public Message readFrom(
+                Class<Message> type,
+                Type genericType, Annotation[] annotations,
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException, WebApplicationException {
+            return clientRequestProvider.get() != null
+                    ? new Message(readFromAsString(entityStream, mediaType)) : new Message("failed");
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == Message.class && mediaType.equals(MediaType.TEXT_PLAIN_TYPE);
+        }
+
+        @Override
+        public void writeTo(
+                Message message,
+                Class<?> type,
+                Type genericType,
+                Annotation[] annotations,
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException, WebApplicationException {
+            writeToAsString((clientRequestProvider.get() != null) ? message.text : "failed", entityStream, mediaType);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SimpleResource.class);
+    }
+
+    @Test
+    public void testReadAfterClose() {
+        final Invocation.Builder request = target().path("simple").register(ScopedMessageEntityProvider.class).request();
+
+        final Response response = request.get(Response.class);
+        // reading entity "out-of-scope"
+        final Message msg = response.readEntity(Message.class);
+        assertEquals("passed", msg.text);
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseCloseTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseCloseTest.java
new file mode 100644
index 0000000..48ab839
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseCloseTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/**
+ * Test for Response.close() method.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ResponseCloseTest extends JerseyTest {
+
+    @Path("simple")
+    public static class SimpleResource {
+
+        @GET
+        @Produces("text/plain")
+        public String getIt() {
+            return "it";
+        }
+
+        @GET
+        @Path("empty")
+        public Response getEmpty() {
+            return Response.noContent().build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SimpleResource.class);
+    }
+
+    @Test
+    public void testReadAfterClose() {
+        final Response response = target().path("simple").request().get(Response.class);
+        response.close();
+        try {
+            response.readEntity(String.class);
+            fail("IllegalStateException expected when reading entity after response has been closed.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testCloseBeforeReadingEmptyContentHasNoEffect() {
+        final Response response = target().path("simple").path("empty").request().get(Response.class);
+        response.close();
+        try {
+            response.readEntity(String.class);
+            fail("IllegalStateException expected when reading entity after response has been closed.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testReadAfterMultipleClose() {
+        final Response response = target().path("simple").request().get(Response.class);
+        response.close();
+        response.close();
+        response.close();
+        try {
+            response.readEntity(String.class);
+            fail("IllegalStateException expected when reading entity after response has been closed.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testReadBufferedEntityAfterClose() {
+        final Response response = target().path("simple").request().get(Response.class);
+        response.bufferEntity();
+        response.close();
+        try {
+            response.readEntity(String.class);
+            fail("IllegalStateException expected when reading a buffered entity after response has been closed.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testBufferEntityAfterClose() {
+        final Response response = target().path("simple").request().get(Response.class);
+        response.close();
+        try {
+            response.bufferEntity();
+            fail("IllegalStateException expected when reading a buffered entity after response has been closed.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testGetEntityAfterClose() {
+        final Response response = target().path("simple").request().get(Response.class);
+        response.close();
+        try {
+            response.getEntity();
+            fail("IllegalStateException expected when reading a buffered entity after response has been closed.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testHasEntityAfterClose() {
+        final Response response = target().path("simple").request().get(Response.class);
+        response.close();
+        try {
+            response.hasEntity();
+            fail("IllegalStateException should have been caught inside hasEntity.");
+        } catch (final IllegalStateException ex) {
+            // expected
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
new file mode 100644
index 0000000..c9ddfae
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Buffered response entity tests.
+ *
+ * @author Michal Gajdos
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ResponseReadAndBufferEntityTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(ResponseReadAndBufferEntityTest.class.getName());
+
+    public static class CorruptableInputStream extends InputStream {
+
+        private final AtomicInteger closeCounter = new AtomicInteger(0);
+
+        private boolean corruptClose = false;
+        private boolean corruptRead = false;
+
+        private final ByteArrayInputStream delegate;
+
+        public CorruptableInputStream() {
+            this.delegate = new ByteArrayInputStream(Resource.ENTITY.getBytes());
+        }
+
+        @Override
+        public synchronized int read() throws IOException {
+            if (corruptRead) {
+                corrupt();
+            }
+            return delegate.read();
+        }
+
+        @Override
+        public int read(final byte[] b) throws IOException {
+            if (corruptRead) {
+                corrupt();
+            }
+            return delegate.read(b);
+        }
+
+        @Override
+        public int read(final byte[] b, final int off, final int len) throws IOException {
+            if (corruptRead) {
+                corrupt();
+            }
+            return delegate.read(b, off, len);
+        }
+
+        @Override
+        public long skip(final long n) throws IOException {
+            if (corruptRead) {
+                corrupt();
+            }
+            return delegate.skip(n);
+        }
+
+        @Override
+        public int available() throws IOException {
+            if (corruptRead) {
+                corrupt();
+            }
+            return delegate.available();
+        }
+
+        @Override
+        public boolean markSupported() {
+            return delegate.markSupported();
+        }
+
+        @Override
+        public void mark(final int readAheadLimit) {
+            delegate.mark(readAheadLimit);
+        }
+
+        @Override
+        public void reset() {
+            closeCounter.set(0);
+            delegate.reset();
+        }
+
+        @Override
+        public void close() throws IOException {
+            closeCounter.incrementAndGet();
+            if (corruptClose) {
+                corrupt();
+            }
+            delegate.close();
+        }
+
+        public void setCorruptRead(final boolean corruptRead) {
+            this.corruptRead = corruptRead;
+        }
+
+        public void setCorruptClose(final boolean corruptClose) {
+            this.corruptClose = corruptClose;
+        }
+
+        public int getCloseCount() {
+            return closeCounter.get();
+        }
+
+        private static void corrupt() throws IOException {
+            throw new IOException("Apocalypse Now");
+        }
+    }
+
+    @Path("response")
+    public static class Resource {
+
+        public static final String ENTITY = "ENtiTy";
+
+        @GET
+        @Path("corrupted")
+        public CorruptableInputStream corrupted() {
+            return new CorruptableInputStream();
+        }
+
+        @GET
+        @Path("string")
+        public String string() {
+            return ENTITY;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(Resource.class)
+                .registerInstances(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    @Test
+    public void testBufferEntityReadsOriginalStreamTest() throws Exception {
+        final WebTarget target = target("response/corrupted");
+        final CorruptableInputStream cis = new CorruptableInputStream();
+        target.register(new ClientResponseFilter() {
+
+            @Override
+            public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) {
+                responseContext.setEntityStream(cis);
+            }
+
+        });
+        final Response response = target.request().buildGet().invoke();
+
+        try {
+            cis.setCorruptRead(true);
+            response.bufferEntity();
+            fail("ProcessingException expected.");
+        } catch (ProcessingException pe) {
+            // expected
+            assertThat(pe.getCause(), instanceOf(IOException.class));
+        }
+    }
+
+    @Test
+    // See JERSEY-1340
+    public void testSecondUnbufferedRead() throws Exception {
+        final Response response = target("response/string").request(MediaType.TEXT_PLAIN).get();
+        String entity = response.readEntity(String.class);
+        assertEquals(Resource.ENTITY, entity);
+
+        try {
+            response.readEntity(Reader.class);
+            fail("IllegalStateException expected to be thrown.");
+        } catch (IllegalStateException expected) {
+            // passed.
+        }
+    }
+
+    @Test
+    // See JERSEY-1339
+    public void testSecondBufferedRead() throws Exception {
+        final Response response = target("response/string").request(MediaType.TEXT_PLAIN).get();
+        response.bufferEntity();
+
+        String entity;
+
+        entity = response.readEntity(String.class);
+        assertEquals(Resource.ENTITY, entity);
+
+        entity = response.readEntity(String.class);
+        assertEquals(Resource.ENTITY, entity);
+
+        BufferedReader buffered = new BufferedReader(response.readEntity(Reader.class));
+        String line = buffered.readLine();
+        assertEquals(Resource.ENTITY, line);
+
+        byte[] buffer = new byte[0];
+        buffer = response.readEntity(buffer.getClass());
+        String entityFromBytes = new String(buffer);
+        assertEquals(Resource.ENTITY, entityFromBytes);
+    }
+
+    /**
+     * This method tests behavior of input stream operations in case the underlying input stream throws an exception when closed.
+     * Reproducer for JRFCAF-1344.
+     * <p>
+     * UC-1 : Read unbuffered entity and then try to close the context
+     */
+    @Test
+    public void testReadUnbufferedEntityFromStreamThatFailsToClose() throws Exception {
+
+        final CorruptableInputStream entityStream = new CorruptableInputStream();
+        final WebTarget target = target("response/corrupted");
+        target.register(new ClientResponseFilter() {
+
+            @Override
+            public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) {
+                responseContext.setEntityStream(entityStream);
+            }
+
+        });
+        final Response response = target.request().buildGet().invoke();
+        entityStream.setCorruptClose(true);
+
+        // Read entity should not fail - we silently consume the underlying IOException from closed input stream.
+        final String entity = response.readEntity(String.class, null);
+        assertThat("Unexpected response.", entity.toString(), equalTo(Resource.ENTITY));
+        assertEquals("Close not invoked on underlying input stream.", 1, entityStream.getCloseCount());
+
+        // Close should not fail and should be idempotent
+        response.close();
+        response.close();
+        response.close();
+        assertEquals("Close invoked too many times on underlying input stream.", 1, entityStream.getCloseCount());
+
+        try {
+            // UC-1.1 : Try to read an unbuffered entity from a closed context
+            response.readEntity(String.class, null);
+            fail("IllegalStateException expected when reading from a closed context.");
+            // UC-1.1 : END
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+    }
+
+    /**
+     * This method tests behavior of input stream operations in case the underlying input stream throws an exception when closed.
+     * Reproducer for JRFCAF-1344.
+     * <p>
+     * UC-2 : Read buffered entity multiple times and then try to close the context
+     */
+    @Test
+    public void testReadBufferedEntityMultipleTimesFromStreamThatFailsToClose() throws Exception {
+        final CorruptableInputStream entityStream = new CorruptableInputStream();
+        final WebTarget target = target("response/corrupted");
+        target.register(new ClientResponseFilter() {
+
+            @Override
+            public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) {
+                responseContext.setEntityStream(entityStream);
+            }
+
+        });
+        final Response response = target.request().buildGet().invoke();
+        entityStream.setCorruptClose(true);
+
+        response.bufferEntity();
+        assertEquals("Close not invoked on underlying input stream.", 1, entityStream.getCloseCount());
+
+        String entity;
+        entity = response.readEntity(String.class, null);
+        assertThat("Unexpected response.", entity.toString(), equalTo(Resource.ENTITY));
+        entity = response.readEntity(String.class, null);
+        assertThat("Unexpected response.", entity.toString(), equalTo(Resource.ENTITY));
+
+        // Close should not fail and should be idempotent
+        response.close();
+        response.close();
+        response.close();
+        assertEquals("Close invoked too many times on underlying input stream.", 1, entityStream.getCloseCount());
+
+        try {
+            // UC-2.1 : Try to read a buffered entity from a closed context
+            response.readEntity(String.class, null);
+            fail("IllegalStateException expected when reading from a closed buffered context.");
+            // UC-2.1 : END
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+        // UC-2 : END
+
+        entityStream.reset();
+
+    }
+
+    /**
+     * This method tests behavior of input stream operations in case the underlying input stream throws an exception when closed.
+     * Reproducer for JRFCAF-1344.
+     * <p>
+     * UC-3 : Try to close the response - underlying exception should be reported.
+     */
+    @Test
+    public void testCloseUnreadResponseWithEntityStreamThatFailsToClose() throws Exception {
+        final CorruptableInputStream entityStream = new CorruptableInputStream();
+        final WebTarget target = target("response/corrupted");
+        target.register(new ClientResponseFilter() {
+
+            @Override
+            public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) {
+                responseContext.setEntityStream(entityStream);
+            }
+
+        });
+        final Response response = target.request().buildGet().invoke();
+        entityStream.setCorruptClose(true);
+
+        try {
+            response.close();
+            fail("ProcessingException expected when closing the context and underlying stream throws an IOException.");
+        } catch (ProcessingException pe) {
+            assertThat(pe.getCause(), instanceOf(IOException.class));
+        }
+    }
+
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ShutdownHookMemoryLeakTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ShutdownHookMemoryLeakTest.java
new file mode 100644
index 0000000..eff4b5d
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/ShutdownHookMemoryLeakTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2015, 2018 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.client;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientLifecycleListener;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Ensure Jersey connectors do not leak resources
+ * in case multiple client runtime instances are being created.
+ *
+ * On my laptop, 1000 iterations was sufficient to cause
+ * a memory leak until JERSEY-2688 got fixed.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ShutdownHookMemoryLeakTest extends JerseyTest {
+
+    private static final String PATH = "test";
+    private static final int ITERATIONS = 1000;
+
+    private final ConnectorProvider connectorProvider;
+
+    public ShutdownHookMemoryLeakTest(final ConnectorProvider cp) {
+        connectorProvider = cp;
+    }
+
+
+    @Parameterized.Parameters
+    public static List<ConnectorProvider[]> connectionProviders() {
+        return Arrays.asList(new ConnectorProvider[][] {
+                {new GrizzlyConnectorProvider()},
+                {new JettyConnectorProvider()},
+                {new ApacheConnectorProvider()},
+                {new HttpUrlConnectorProvider()}
+        });
+    }
+
+    @Path(PATH)
+    public static class TestResource {
+
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(connectorProvider);
+    }
+
+    @Test
+    @Ignore("Unstable, ignored for now")
+    public void testClientDoesNotLeakResources() throws Exception {
+
+        final AtomicInteger listenersInitialized = new AtomicInteger(0);
+        final AtomicInteger listenersClosed = new AtomicInteger(0);
+
+        for (int i = 0; i < ITERATIONS; i++) {
+            final Response response = target(PATH).property("another", "runtime").register(new ClientLifecycleListener() {
+                @Override
+                public void onInit() {
+                    listenersInitialized.incrementAndGet();
+                }
+
+                @Override
+                public void onClose() {
+                    listenersClosed.incrementAndGet();
+                }
+            }).register(LoggingFeature.class).request().get();
+            assertEquals("GET", response.readEntity(String.class));
+        }
+
+        Collection shutdownHooks = getShutdownHooks(client());
+
+        assertThat(String.format(
+                    "%s: number of initialized listeners should be the same as number of total request count",
+                        connectorProvider.getClass()),
+                listenersInitialized.get(), is(ITERATIONS));
+
+//      the following check is fragile, as GC could break it easily
+//        assertThat(String.format(
+//                "%s: number of closed listeners should correspond to the number of missing hooks",
+//                        connectorProvider.getClass()),
+//                listenersClosed.get(), is(ITERATIONS - shutdownHooks.size()));
+
+        client().close();      // clean up the rest
+
+        assertThat(String.format(
+                        "%s: number of closed listeners should be the same as the number of total requests made",
+                        connectorProvider.getClass()),
+                listenersClosed.get(), is(ITERATIONS));
+    }
+
+    private Collection getShutdownHooks(javax.ws.rs.client.Client client) throws NoSuchFieldException, IllegalAccessException {
+        JerseyClient jerseyClient = (JerseyClient) client;
+        Field shutdownHooksField = JerseyClient.class.getDeclaredField("shutdownHooks");
+        shutdownHooksField.setAccessible(true);
+        return (Collection) shutdownHooksField.get(jerseyClient);
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/TimeoutTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/TimeoutTest.java
new file mode 100644
index 0000000..507321f
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/TimeoutTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2012, 2018 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.client;
+
+import java.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Martin Matula
+ */
+public class TimeoutTest extends JerseyTest {
+    @Path("/test")
+    public static class TimeoutResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @GET
+        @Path("timeout")
+        public String getTimeout() {
+            try {
+                Thread.sleep(5000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            return "GET";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TimeoutResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(ClientProperties.READ_TIMEOUT, 2000);
+    }
+
+    @Test
+    public void testFast() {
+        Response r = target("test").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("GET", r.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlow() {
+        try {
+            target("test/timeout").request().get();
+            fail("Timeout expected.");
+        } catch (ProcessingException e) {
+            if (!(e.getCause() instanceof IOException)) {
+                e.printStackTrace();
+                fail();
+            }
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java
new file mode 100644
index 0000000..3886ef1
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HttpPatchTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017, 2018 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.client.connector;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.PATCH;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class HttpPatchTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(RequestHeaderModificationsTest.class.getName());
+
+    @Parameterized.Parameters(name = "{index}: {0}")
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][]{
+                // {HttpUrlConnectorProvider.class}, // cannot process PATCH without additional configuration
+                {GrizzlyConnectorProvider.class},
+                {JettyConnectorProvider.class}, // unstable.
+                {ApacheConnectorProvider.class},
+                {GrizzlyConnectorProvider.class},
+                {NettyConnectorProvider.class},
+                {JdkConnectorProvider.class},
+                });
+    }
+
+    private final ConnectorProvider connectorProvider;
+
+    public HttpPatchTest(Class<? extends ConnectorProvider> connectorProviderClass)
+            throws IllegalAccessException, InstantiationException {
+        this.connectorProvider = connectorProviderClass.newInstance();
+    }
+
+    @Override
+    protected Application configure() {
+        set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(PatchResource.class)
+                .register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.HEADERS_ONLY));
+    }
+
+    @Override
+    protected void configureClient(ClientConfig clientConfig) {
+        clientConfig.connectorProvider(connectorProvider);
+    }
+
+    @Test
+    public void testPatchResponse() throws Exception {
+        Response response = target().request().method("PATCH", Entity.text("patch"));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("patch", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPatchEntity() throws Exception {
+        String response = target().request().method("PATCH", Entity.text("patch"), String.class);
+
+        assertEquals("patch", response);
+    }
+
+    @Test
+    public void testPatchGenericType() throws Exception {
+        String response = target().request().method("PATCH", Entity.text("patch"), new GenericType<String>() {
+        });
+
+        assertEquals("patch", response);
+    }
+
+    @Test
+    public void testAsyncPatchResponse() throws Exception {
+        Future<Response> response = target().request().async().method("PATCH", Entity.text("patch"));
+
+        assertEquals(200, response.get().getStatus());
+        assertEquals("patch", response.get().readEntity(String.class));
+    }
+
+    @Test
+    public void testAsyncPatchEntity() throws Exception {
+        Future<String> response = target().request().async().method("PATCH", Entity.text("patch"), String.class);
+
+        assertEquals("patch", response.get());
+    }
+
+    @Test
+    public void testAsyncPatchGenericType() throws Exception {
+        Future<String> response = target().request().async().method("PATCH", Entity.text("patch"), new GenericType<String>() {
+        });
+
+        assertEquals("patch", response.get());
+    }
+
+    @Test
+    public void testRxPatchResponse() throws Exception {
+        CompletionStage<Response> response = target().request().rx().method("PATCH", Entity.text("patch"));
+
+        assertEquals(200, response.toCompletableFuture().get().getStatus());
+        assertEquals("patch", response.toCompletableFuture().get().readEntity(String.class));
+    }
+
+    @Test
+    public void testRxPatchEntity() throws Exception {
+        CompletionStage<String> response = target().request().rx().method("PATCH", Entity.text("patch"), String.class);
+
+        assertEquals("patch", response.toCompletableFuture().get());
+    }
+
+    @Test
+    public void testRxPatchGenericType() throws Exception {
+        CompletionStage<String> response = target().request().rx()
+                                                   .method("PATCH", Entity.text("patch"), new GenericType<String>() {
+                                                   });
+
+        assertEquals("patch", response.toCompletableFuture().get());
+    }
+
+    @Path("/")
+    public static class PatchResource {
+
+        @PATCH
+        public String patch(String entity) {
+
+            System.out.println("SERVER: patch request received.");
+
+            return entity;
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java
new file mode 100644
index 0000000..2fab4dc
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2014, 2018 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.client.connector;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+/**
+ * JERSEY-2206 reproducer
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class RequestHeaderModificationsTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(RequestHeaderModificationsTest.class.getName());
+    private static final boolean GZIP = false; // change to true when JERSEY-2341 fixed
+    private static final boolean DUMP_ENTITY = false; // I have troubles to dump entity with async jetty!
+
+    private static final String QUESTION = "QUESTION";
+    private static final String ANSWER = "ANSWER";
+    private static final String REQUEST_HEADER_NAME_CLIENT = "Client-Prop";
+    private static final String REQUEST_HEADER_VALUE_CLIENT = "Client-Value";
+    private static final String REQUEST_HEADER_NAME_FILTER = "Filter-Prop";
+    private static final String REQUEST_HEADER_VALUE_FILTER = "Filter-Value";
+    private static final String REQUEST_HEADER_NAME_INTERCEPTOR = "Iceptor-Prop";
+    private static final String REQUEST_HEADER_VALUE_INTERCEPTOR = "Iceptor-Value";
+    private static final String REQUEST_HEADER_NAME_MBW = "Mbw-Prop";
+    private static final String REQUEST_HEADER_VALUE_MBW = "Mbw-Value";
+    private static final String REQUEST_HEADER_MODIFICATION_SUPPORTED = "modificationSupported";
+    private static final String PATH = "/resource";
+
+    @Parameterized.Parameters(name = "{index}: {0} / modificationSupported= {1} / addHeader= {2}")
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {HttpUrlConnectorProvider.class, true, false},
+                {GrizzlyConnectorProvider.class, false, false}, // change to true when JERSEY-2341 fixed
+                {JettyConnectorProvider.class, false, false}, // change to true when JERSEY-2341 fixed
+                {ApacheConnectorProvider.class, false, false}, // change to true when JERSEY-2341 fixed
+                {HttpUrlConnectorProvider.class, true, true},
+                {GrizzlyConnectorProvider.class, false, true}, // change to true when JERSEY-2341 fixed
+                {JettyConnectorProvider.class, false, true}, // change to true when JERSEY-2341 fixed
+                {ApacheConnectorProvider.class, false, true}, // change to true when JERSEY-2341 fixed
+        });
+    }
+
+    private final ConnectorProvider connectorProvider;
+    private final boolean modificationSupported; // remove when JERSEY-2341 fixed
+    private final boolean addHeader;
+
+    public RequestHeaderModificationsTest(Class<? extends ConnectorProvider> connectorProviderClass,
+                                          boolean modificationSupported, boolean addHeader)
+            throws IllegalAccessException, InstantiationException {
+        this.connectorProvider = connectorProviderClass.newInstance();
+        this.modificationSupported = modificationSupported;
+        this.addHeader = addHeader;
+    }
+
+    @Override
+    protected Application configure() {
+        set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+
+        enable(TestProperties.LOG_TRAFFIC);
+        if (DUMP_ENTITY) {
+            enable(TestProperties.DUMP_ENTITY);
+        }
+        return new ResourceConfig(TestResource.class).register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.HEADERS_ONLY));
+    }
+
+    @Override
+    protected void configureClient(ClientConfig clientConfig) {
+        clientConfig.register(MyClientRequestFilter.class);
+        clientConfig.register(new MyWriterInterceptor(addHeader));
+        clientConfig.register(new MyMessageBodyWriter(addHeader));
+        clientConfig.connectorProvider(connectorProvider);
+    }
+
+    @Test
+    public void testWarningLogged() throws Exception {
+        Response response = requestBuilder().post(requestEntity());
+        assertResponse(response);
+    }
+
+    @Test
+    public void testWarningLoggedAsync() throws Exception {
+        AsyncInvoker asyncInvoker = requestBuilder().async();
+        Future<Response> responseFuture = asyncInvoker.post(requestEntity());
+        Response response = responseFuture.get();
+        assertResponse(response);
+    }
+
+    private Invocation.Builder requestBuilder() {
+        return target(PATH)
+                .request()
+                .header(REQUEST_HEADER_NAME_CLIENT, REQUEST_HEADER_VALUE_CLIENT)
+                .header(REQUEST_HEADER_MODIFICATION_SUPPORTED, modificationSupported && addHeader)
+                .header("hello", "double").header("hello", "value");
+    }
+
+    private Entity<MyEntity> requestEntity() {
+        return Entity.text(new MyEntity(QUESTION));
+    }
+
+    private void assertResponse(Response response) {
+        if (!modificationSupported) {
+            final String UNSENT_HEADER_CHANGES = "Unsent header changes";
+            LogRecord logRecord = findLogRecord(UNSENT_HEADER_CHANGES);
+            if (addHeader) {
+                assertNotNull("Missing LogRecord for message '" + UNSENT_HEADER_CHANGES + "'.", logRecord);
+                assertThat(logRecord.getMessage(), containsString(REQUEST_HEADER_NAME_INTERCEPTOR));
+                assertThat(logRecord.getMessage(), containsString(REQUEST_HEADER_NAME_MBW));
+            } else {
+                assertNull("Unexpected LogRecord for message '" + UNSENT_HEADER_CHANGES + "'.", logRecord);
+            }
+        }
+
+        assertEquals(200, response.getStatus());
+        assertEquals(ANSWER, response.readEntity(String.class));
+    }
+
+    private LogRecord findLogRecord(String messageContains) {
+        for (final LogRecord record : getLoggedRecords()) {
+            if (record.getMessage().contains(messageContains)) {
+                return record;
+            }
+        }
+        return null;
+    }
+
+    @Path(PATH)
+    public static class TestResource {
+
+        @POST
+        public String handle(InputStream questionStream,
+                             @HeaderParam(REQUEST_HEADER_NAME_CLIENT) String client,
+                             @HeaderParam(REQUEST_HEADER_NAME_FILTER) String filter,
+                             @HeaderParam(REQUEST_HEADER_NAME_INTERCEPTOR) String interceptor,
+                             @HeaderParam(REQUEST_HEADER_NAME_MBW) String mbw,
+                             @HeaderParam(REQUEST_HEADER_MODIFICATION_SUPPORTED) boolean modificationSupported)
+                throws IOException {
+            assertEquals(REQUEST_HEADER_VALUE_CLIENT, client);
+            assertEquals(REQUEST_HEADER_VALUE_FILTER, filter);
+            if (modificationSupported) {
+                assertEquals(REQUEST_HEADER_VALUE_INTERCEPTOR, interceptor);
+                assertEquals(REQUEST_HEADER_VALUE_MBW, mbw);
+            }
+            assertEquals(QUESTION, new Scanner(GZIP ? new GZIPInputStream(questionStream) : questionStream).nextLine());
+            return ANSWER;
+        }
+    }
+
+    public static class MyWriterInterceptor implements WriterInterceptor {
+
+        private final boolean addHeader;
+
+        public MyWriterInterceptor(boolean addHeader) {
+            this.addHeader = addHeader;
+        }
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            if (addHeader) {
+                context.getHeaders().add(REQUEST_HEADER_NAME_INTERCEPTOR, REQUEST_HEADER_VALUE_INTERCEPTOR);
+            }
+            if (GZIP) {
+                context.setOutputStream(new GZIPOutputStream(context.getOutputStream()));
+            }
+            context.proceed();
+        }
+    }
+
+    public static class MyClientRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext) throws IOException {
+            requestContext.getHeaders().add(REQUEST_HEADER_NAME_FILTER, REQUEST_HEADER_VALUE_FILTER);
+        }
+    }
+
+    @Priority(Priorities.ENTITY_CODER)
+    public static class MyMessageBodyWriter implements MessageBodyWriter<MyEntity> {
+
+        private final boolean addHeader;
+
+        public MyMessageBodyWriter(boolean addHeader) {
+            this.addHeader = addHeader;
+        }
+
+        @Override
+        public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(MyEntity o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1; //ignored
+        }
+
+        @Override
+        public void writeTo(MyEntity o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            if (addHeader) {
+                httpHeaders.add(REQUEST_HEADER_NAME_MBW, REQUEST_HEADER_VALUE_MBW);
+            }
+            entityStream.write(o.getValue().getBytes());
+        }
+    }
+
+    public static class MyEntity {
+
+        private String value;
+
+        public MyEntity() {
+        }
+
+        public MyEntity(String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+    }
+
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
new file mode 100644
index 0000000..e0315f4
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, 2018 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.client.connector.ssl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLContext;
+
+import org.glassfish.jersey.SslConfigurator;
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import com.google.common.io.ByteStreams;
+
+/**
+ * SSL connector hostname verification tests.
+ *
+ * @author Petr Bouda
+ */
+@RunWith(Parameterized.class)
+public abstract class AbstractConnectorServerTest {
+
+    // Default truststore and keystore
+    private static final String CLIENT_TRUST_STORE = "truststore-localhost-client";
+    private static final String SERVER_KEY_STORE = "keystore-localhost-server";
+    private static final String CLIENT_KEY_STORE = "keystore-client";
+
+    /**
+     * Test parameters provider.
+     *
+     * @return test parameters.
+     */
+    @Parameterized.Parameters(name = "{index}: {0}")
+    public static Iterable<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {new HttpUrlConnectorProvider()},
+                {new GrizzlyConnectorProvider()},
+                {new JettyConnectorProvider()},
+                {new ApacheConnectorProvider()}
+        });
+    }
+
+    @Parameterized.Parameter(0)
+    public ConnectorProvider connectorProvider;
+
+    private final Object serverGuard = new Object();
+    private Server server = null;
+
+    @Before
+    public void setUp() throws Exception {
+        synchronized (serverGuard) {
+            if (server != null) {
+                throw new IllegalStateException(
+                        "Test run sync issue: Another instance of the SSL-secured HTTP test server has been already started.");
+            }
+            server = Server.start(serverKeyStore());
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        synchronized (serverGuard) {
+            if (server == null) {
+                throw new IllegalStateException("Test run sync issue: There is no SSL-secured HTTP test server to stop.");
+            }
+            server.stop();
+            server = null;
+        }
+    }
+
+    protected SSLContext getSslContext() throws IOException {
+        final InputStream trustStore = SslConnectorConfigurationTest.class.getResourceAsStream(clientTrustStore());
+        final InputStream keyStore = SslConnectorConfigurationTest.class.getResourceAsStream(CLIENT_KEY_STORE);
+        return SslConfigurator.newInstance()
+                .trustStoreBytes(ByteStreams.toByteArray(trustStore))
+                .trustStorePassword("asdfgh")
+                .keyStoreBytes(ByteStreams.toByteArray(keyStore))
+                .keyPassword("asdfgh")
+                .createSSLContext();
+    }
+
+    protected String serverKeyStore() {
+        return SERVER_KEY_STORE;
+    }
+
+    protected String clientTrustStore() {
+        return CLIENT_TRUST_STORE;
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationException.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationException.java
new file mode 100644
index 0000000..f76cf85
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector.ssl;
+
+/**
+ * A runtime exception representing a failure to provide correct authentication credentials.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class AuthenticationException extends RuntimeException {
+
+    /**
+     * Create new authentication exception.
+     *
+     * @param message error message.
+     * @param realm   security realm.
+     */
+    public AuthenticationException(String message, String realm) {
+        super(message);
+        this.realm = realm;
+    }
+
+    private String realm = null;
+
+    /**
+     * Get security realm.
+     *
+     * @return security realm.
+     */
+    public String getRealm() {
+        return this.realm;
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationExceptionMapper.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationExceptionMapper.java
new file mode 100644
index 0000000..53912f8
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationExceptionMapper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector.ssl;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Map an authentication exception to an HTTP 401 response,
+ * optionally including the realm for a credentials challenge at the client.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Provider
+public class AuthenticationExceptionMapper implements ExceptionMapper<AuthenticationException> {
+
+    @Override
+    public Response toResponse(AuthenticationException e) {
+        if (e.getRealm() != null) {
+            return Response
+                    .status(Status.UNAUTHORIZED)
+                    .header("WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\"")
+                    .type("text/plain")
+                    .entity(e.getMessage())
+                    .build();
+        } else {
+            return Response
+                    .status(Status.UNAUTHORIZED)
+                    .type("text/plain")
+                    .entity(e.getMessage())
+                    .build();
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/RootResource.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/RootResource.java
new file mode 100644
index 0000000..bccbf94
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/RootResource.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector.ssl;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+
+import org.glassfish.jersey.internal.util.Base64;
+
+/**
+ * Simple resource demonstrating low level approach of getting user credentials.
+ *
+ * A better way would be injecting {@link javax.ws.rs.core.SecurityContext}.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("/")
+public class RootResource {
+    private static final Logger LOGGER = Logger.getLogger(RootResource.class.getName());
+    /**
+     * Served content.
+     */
+    public static final String CONTENT = "JERSEY HTTPS EXAMPLE\n";
+
+    /**
+     * Serve content.
+     *
+     * @param headers request headers.
+     * @return content (see {@link #CONTENT}).
+     */
+    @GET
+    public String getContent(@Context HttpHeaders headers) {
+        // you can get username form HttpHeaders
+        LOGGER.info("Service: GET / User: " + getUser(headers));
+
+        return CONTENT;
+    }
+
+    private String getUser(HttpHeaders headers) {
+        // this is a very minimalistic and "naive" code;
+        // if you plan to use it, add the necessary checks
+        String auth = headers.getRequestHeader("authorization").get(0);
+
+        auth = auth.substring("Basic ".length());
+        String[] values = Base64.decodeAsString(auth).split(":");
+
+        // String username = values[0];
+        // String password = values[1];
+        return values[0];
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SecurityFilter.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SecurityFilter.java
new file mode 100644
index 0000000..128d4cf
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SecurityFilter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector.ssl;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.logging.Logger;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.internal.util.Base64;
+import org.glassfish.jersey.server.ContainerRequest;
+
+/**
+ * Simple authentication filter.
+ *
+ * Returns response with http status 401 when proper authentication is not provided in incoming request.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @see ContainerRequestFilter
+ */
+@Provider
+@PreMatching
+public class SecurityFilter implements ContainerRequestFilter {
+
+    /**
+     * Security realm.
+     */
+    public static final String REALM = "Test HTTPS Authentication REALM";
+    private static final Logger LOGGER = Logger.getLogger(SecurityFilter.class.getName());
+
+    @Context
+    private UriInfo uriInfo;
+
+    @Override
+    public void filter(ContainerRequestContext filterContext) throws IOException {
+        User user = authenticate(filterContext.getRequest());
+        filterContext.setSecurityContext(new AuthorizationContext(user));
+    }
+
+    private User authenticate(Request request) {
+        // Extract authentication credentials
+        String authentication = ((ContainerRequest) request).getHeaderString(HttpHeaders.AUTHORIZATION);
+        if (authentication == null) {
+            throw new AuthenticationException("Authentication credentials are required", REALM);
+        }
+        if (!authentication.startsWith("Basic ")) {
+            return null;
+            // additional checks should be done here
+            // "Only HTTP Basic authentication is supported"
+        }
+        authentication = authentication.substring("Basic ".length());
+        String[] values = Base64.decodeAsString(authentication).split(":");
+        if (values.length < 2) {
+            throw new WebApplicationException(400);
+            // "Invalid syntax for username and password"
+        }
+        String username = values[0];
+        String password = values[1];
+        if ((username == null) || (password == null)) {
+            throw new WebApplicationException(400);
+            // "Missing username or password"
+        }
+
+        // Validate the extracted credentials
+        User user;
+
+        if ("user".equals(username) && "password".equals(password)) {
+            user = new User("user", "user");
+            LOGGER.info("USER AUTHENTICATED");
+        } else {
+            LOGGER.info("USER NOT AUTHENTICATED");
+            throw new AuthenticationException("Invalid username or password", REALM);
+        }
+        return user;
+    }
+
+    private class AuthorizationContext implements SecurityContext {
+
+        private final User user;
+        private final Principal principal;
+
+        public AuthorizationContext(final User user) {
+            this.user = user;
+            this.principal = new Principal() {
+
+                @Override
+                public String getName() {
+                    return user.username;
+                }
+            };
+        }
+
+        @Override
+        public Principal getUserPrincipal() {
+            return this.principal;
+        }
+
+        @Override
+        public boolean isUserInRole(String role) {
+            return (role.equals(user.role));
+        }
+
+        @Override
+        public boolean isSecure() {
+            return "https".equals(uriInfo.getRequestUri().getScheme());
+        }
+
+        @Override
+        public String getAuthenticationScheme() {
+            return SecurityContext.BASIC_AUTH;
+        }
+    }
+
+    private static class User {
+
+        public String username;
+        public String role;
+
+        public User(String username, String role) {
+            this.username = username;
+            this.role = role;
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
new file mode 100644
index 0000000..ca3cb39
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector.ssl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.ssl.SSLContextConfigurator;
+import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+
+import com.google.common.io.ByteStreams;
+
+/**
+ * A simple SSL-secured HTTP server for testing purposes.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+final class Server {
+
+    private static final Logger LOGGER = Logger.getLogger(Server.class.getName());
+
+    private static final String SERVER_TRUST_STORE = "truststore-server";
+
+    /**
+     * Base server URI.
+     */
+    public static final URI BASE_URI = getBaseURI();
+
+    private final HttpServer webServer;
+
+    private Server(final HttpServer webServer) {
+        this.webServer = webServer;
+    }
+
+    private static URI getBaseURI() {
+        return UriBuilder.fromUri("https://localhost/").port(getPort(8463)).build();
+    }
+
+    private static int getPort(int defaultPort) {
+        final String port = System.getProperty("jersey.config.test.container.port");
+        if (null != port) {
+            try {
+                return Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                LOGGER.warning("Value of jersey.config.test.container.port property"
+                        + " is not a valid positive integer [" + port + "]."
+                        + " Reverting to default [" + defaultPort + "].");
+            }
+        }
+        return defaultPort;
+    }
+
+    /**
+     * Start SSL-secured HTTP test server.
+     *
+     * @throws IOException in case there is an error while reading server key store or trust store.
+     * @return an instance of the started SSL-secured HTTP test server.
+     */
+    public static Server start(String keystore) throws IOException {
+        final InputStream trustStore = Server.class.getResourceAsStream(SERVER_TRUST_STORE);
+        final InputStream keyStore = Server.class.getResourceAsStream(keystore);
+
+        // Grizzly ssl configuration
+        SSLContextConfigurator sslContext = new SSLContextConfigurator();
+
+        // set up security context
+        sslContext.setKeyStoreBytes(ByteStreams.toByteArray(keyStore));  // contains server key pair
+        sslContext.setKeyStorePass("asdfgh");
+        sslContext.setTrustStoreBytes(ByteStreams.toByteArray(trustStore)); // contains client certificate
+        sslContext.setTrustStorePass("asdfgh");
+
+        ResourceConfig rc = new ResourceConfig();
+        rc.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+        rc.registerClasses(RootResource.class, SecurityFilter.class, AuthenticationExceptionMapper.class);
+
+        final HttpServer grizzlyServer = GrizzlyHttpServerFactory.createHttpServer(
+                getBaseURI(),
+                rc,
+                true,
+                new SSLEngineConfigurator(sslContext).setClientMode(false).setNeedClientAuth(true)
+        );
+
+        // start Grizzly embedded server //
+        LOGGER.info("Jersey app started. Try out " + BASE_URI + "\nHit CTRL + C to stop it...");
+        grizzlyServer.start();
+
+        return new Server(grizzlyServer);
+    }
+
+    /**
+     * Stop SSL-secured HTTP test server.
+     */
+    public void stop() {
+        webServer.shutdownNow();
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorConfigurationTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorConfigurationTest.java
new file mode 100644
index 0000000..07edcb3
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorConfigurationTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector.ssl;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import javax.net.ssl.SSLContext;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * SSL connector tests.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class SslConnectorConfigurationTest extends AbstractConnectorServerTest {
+
+    /**
+     * Test to see that the correct Http status is returned.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testSSLWithAuth() throws Exception {
+        final SSLContext sslContext = getSslContext();
+
+        final ClientConfig cc = new ClientConfig().connectorProvider(connectorProvider);
+        final Client client = ClientBuilder.newBuilder()
+                .withConfig(cc)
+                .sslContext(sslContext)
+                .build();
+
+        // client basic auth demonstration
+        client.register(HttpAuthenticationFeature.basic("user", "password"));
+        final WebTarget target = client.target(Server.BASE_URI).register(LoggingFeature.class);
+
+        final Response response = target.path("/").request().get(Response.class);
+
+        assertEquals(200, response.getStatus());
+    }
+
+    /**
+     * Test to see that HTTP 401 is returned when client tries to GET without
+     * proper credentials.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testHTTPBasicAuth1() throws Exception {
+        final SSLContext sslContext = getSslContext();
+
+        final ClientConfig cc = new ClientConfig().connectorProvider(new ApacheConnectorProvider());
+        final Client client = ClientBuilder.newBuilder()
+                .withConfig(cc)
+                .sslContext(sslContext)
+                .build();
+
+        final WebTarget target = client.target(Server.BASE_URI).register(LoggingFeature.class);
+
+        final Response response = target.path("/").request().get(Response.class);
+
+        assertEquals(401, response.getStatus());
+    }
+
+    /**
+     * Test to see that SSLHandshakeException is thrown when client don't have
+     * trusted key.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testSSLAuth1() throws Exception {
+        final SSLContext sslContext = getSslContext();
+
+        final ClientConfig cc = new ClientConfig().connectorProvider(new ApacheConnectorProvider());
+        final Client client = ClientBuilder.newBuilder()
+                .withConfig(cc)
+                .sslContext(sslContext)
+                .build();
+
+        WebTarget target = client.target(Server.BASE_URI).register(LoggingFeature.class);
+
+        boolean caught = false;
+        try {
+            target.path("/").request().get(String.class);
+        } catch (Exception e) {
+            caught = true;
+        }
+
+        assertTrue(caught);
+    }
+
+    /**
+     * Test that a response to an authentication challenge has the same SSL configuration as the original request.
+     */
+    @Test
+    public void testSSLWithNonPreemptiveAuth() throws Exception {
+        final SSLContext sslContext = getSslContext();
+
+        final ClientConfig cc = new ClientConfig().connectorProvider(connectorProvider);
+        final Client client = ClientBuilder.newBuilder()
+                .withConfig(cc)
+                .sslContext(sslContext)
+                .build();
+
+        // client basic auth demonstration
+        HttpAuthenticationFeature authFeature = HttpAuthenticationFeature.basicBuilder()
+                .nonPreemptive()
+                .credentials("user", "password")
+                .build();
+
+        client.register(authFeature);
+        final WebTarget target = client.target(Server.BASE_URI).register(LoggingFeature.class);
+
+        final Response response = target.path("/").request().get(Response.class);
+
+        assertEquals(200, response.getStatus());
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java
new file mode 100644
index 0000000..a41f218
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015, 2018 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.client.connector.ssl;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * SSL connector hostname verification tests.
+ *
+ * @author Petr Bouda
+ */
+@RunWith(Parameterized.class)
+public class SslConnectorHostnameVerifierTest extends AbstractConnectorServerTest {
+
+    private static final String CLIENT_TRUST_STORE = "truststore-example_com-client";
+    private static final String SERVER_KEY_STORE = "keystore-example_com-server";
+
+    @Override
+    protected String serverKeyStore() {
+        return SERVER_KEY_STORE;
+    }
+
+    @Override
+    protected String clientTrustStore() {
+        return CLIENT_TRUST_STORE;
+    }
+
+    /**
+     * Test to apply {@link HostnameVerifier} along with SSL in the predefined connectors
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testHostnameVerifierApplied() throws Exception {
+        // Grizzly and Jetty connectors don't support Hostname Verification
+        if (isExcluded(Arrays.asList(GrizzlyConnectorProvider.class, JettyConnectorProvider.class))) {
+            return;
+        }
+
+        final Client client = ClientBuilder.newBuilder()
+                .withConfig(new ClientConfig().connectorProvider(connectorProvider))
+                .register(HttpAuthenticationFeature.basic("user", "password"))
+                .hostnameVerifier(new CustomHostnameVerifier())
+                .sslContext(getSslContext())
+                .build();
+
+        try {
+            client.target(Server.BASE_URI).request().get(Response.class);
+            fail("HostnameVerifier was not applied.");
+        } catch (ProcessingException pex) {
+            CustomHostnameVerifier.HostnameVerifierException hve = getHVE(pex);
+
+            if (hve != null) {
+                assertEquals(CustomHostnameVerifier.EX_VERIFIER_MESSAGE, hve.getMessage());
+            } else {
+                fail("Invalid wrapped exception.");
+            }
+        }
+    }
+
+    private boolean isExcluded(List<Class<? extends ConnectorProvider>> excluded) {
+        for (Class<?> clazz : excluded) {
+            if (clazz.isAssignableFrom(connectorProvider.getClass())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static CustomHostnameVerifier.HostnameVerifierException getHVE(final Throwable stacktrace) {
+        Throwable temp = stacktrace;
+        do {
+            temp = temp.getCause();
+            if (temp instanceof CustomHostnameVerifier.HostnameVerifierException) {
+                return (CustomHostnameVerifier.HostnameVerifierException) temp;
+            }
+        } while (temp != null);
+        return null;
+    }
+
+    public static class CustomHostnameVerifier implements HostnameVerifier {
+
+        private static final String EX_VERIFIER_MESSAGE = "Verifier Applied";
+
+        @Override
+        public boolean verify(final String s, final SSLSession sslSession) {
+            throw new HostnameVerifierException(EX_VERIFIER_MESSAGE);
+        }
+
+        @Override
+        public final String toString() {
+            return "CUSTOM_HOST_VERIFIER";
+        }
+
+        public static class HostnameVerifierException extends RuntimeException {
+
+            public HostnameVerifierException(final String message) {
+                super(message);
+            }
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslHttpUrlConnectorTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslHttpUrlConnectorTest.java
new file mode 100644
index 0000000..7a82321
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslHttpUrlConnectorTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015, 2018 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.client.connector.ssl;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test custom socket factory in HttpUrlConnection using SSL
+ *
+ * @author Petr Bouda
+ */
+public class SslHttpUrlConnectorTest extends AbstractConnectorServerTest {
+
+    /**
+     * Test to see that the correct Http status is returned.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testSSLWithCustomSocketFactory() throws Exception {
+        final SSLContext sslContext = getSslContext();
+        final CustomSSLSocketFactory socketFactory = new CustomSSLSocketFactory(sslContext);
+
+        final ClientConfig cc = new ClientConfig()
+                .connectorProvider(new HttpUrlConnectorProvider().connectionFactory(
+                        new HttpUrlConnectorProvider.ConnectionFactory() {
+                            @Override
+                            public HttpURLConnection getConnection(final URL url) throws IOException {
+                                HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+                                connection.setSSLSocketFactory(socketFactory);
+                                return connection;
+                            }
+                        }));
+
+        final Client client = ClientBuilder.newBuilder()
+                .withConfig(cc)
+                .sslContext(sslContext)
+                .register(HttpAuthenticationFeature.basic("user", "password"))
+                .register(LoggingFeature.class)
+                .build();
+
+        final Response response = client.target(Server.BASE_URI).path("/").request().get();
+        assertEquals(200, response.getStatus());
+        assertTrue(socketFactory.isVisited());
+    }
+
+    public static class CustomSSLSocketFactory extends SSLSocketFactory {
+
+        private boolean visited = false;
+
+        private final SSLContext sslContext;
+
+        protected CustomSSLSocketFactory(SSLContext sslContext) {
+            this.sslContext = sslContext;
+        }
+
+        public boolean isVisited() {
+            return visited;
+        }
+
+        @Override
+        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
+            this.visited = true;
+            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+            return sslSocketFactory.createSocket(s, host, port, autoClose);
+        }
+
+        @Override
+        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
+            throw new UnsupportedOperationException("This createSocket method should not be invoked.");
+        }
+
+        @Override
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+            throw new UnsupportedOperationException("This createSocket method should not be invoked.");
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port) throws IOException {
+            throw new UnsupportedOperationException("This createSocket method should not be invoked.");
+        }
+
+        @Override
+        public Socket createSocket(String host, int port) throws IOException {
+            throw new UnsupportedOperationException("This createSocket method should not be invoked.");
+        }
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            return null;
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return null;
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/grizzlyconnector/NonBlockingTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/grizzlyconnector/NonBlockingTest.java
new file mode 100644
index 0000000..5006b19
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/grizzlyconnector/NonBlockingTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, 2018 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.client.grizzlyconnector;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Grizzly connector non blocking test.
+ */
+public class NonBlockingTest extends JerseyTest {
+
+    @Path("/test")
+    public static class Resource {
+
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new GrizzlyConnectorProvider());
+    }
+
+    private volatile String invocationCallbackThreadName;
+
+    @Test
+    public void testNonBlockingConnector() throws Exception {
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        Future<String> future = target("test")
+                .request()
+                .async()
+                .get(new InvocationCallback<String>() {
+                    @Override
+                    public void completed(String response) {
+                        invocationCallbackThreadName = Thread.currentThread().getName();
+                        countDownLatch.countDown();
+                    }
+
+                    @Override
+                    public void failed(Throwable throwable) {
+                        invocationCallbackThreadName = Thread.currentThread().getName();
+                        countDownLatch.countDown();
+                    }
+                });
+
+        String response = future.get();
+        assertNotNull(response);
+        assertTrue("Invocation callback was not invoked",
+                countDownLatch.await(5, TimeUnit.SECONDS));
+        assertTrue("Invocation callback is not executed on the NIO pool thread.",
+                   !invocationCallbackThreadName.contains("jersey-client-async-executor"));
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/AsyncTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/AsyncTest.java
new file mode 100644
index 0000000..202fe32
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/AsyncTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2013, 2018 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.client.httpurlconnector;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.container.TimeoutHandler;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.Matchers;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Asynchronous connector test.
+ *
+ * @author Arul Dhesiaseelan (aruld at acm.org)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class AsyncTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(AsyncTest.class.getName());
+    private static final String PATH = "async";
+
+    /**
+     * Asynchronous test resource.
+     */
+    @Path(PATH)
+    public static class AsyncResource {
+        /**
+         * Typical long-running operation duration.
+         */
+        public static final long OPERATION_DURATION = 1000;
+
+        /**
+         * Long-running asynchronous post.
+         *
+         * @param asyncResponse async response.
+         * @param id            post request id (received as request payload).
+         */
+        @POST
+        public void asyncPost(@Suspended final AsyncResponse asyncResponse, final String id) {
+            LOGGER.info("Long running post operation called with id " + id + " on thread " + Thread.currentThread().getName());
+            new Thread(new Runnable() {
+
+                @Override
+                public void run() {
+                    final String result = veryExpensiveOperation();
+                    asyncResponse.resume(result);
+                }
+
+                private String veryExpensiveOperation() {
+                    // ... very expensive operation that typically finishes within 1 seconds, simulated using sleep()
+                    try {
+                        Thread.sleep(OPERATION_DURATION);
+                        return "DONE-" + id;
+                    } catch (final InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                        return "INTERRUPTED-" + id;
+                    } finally {
+                        LOGGER.info("Long running post operation finished on thread " + Thread.currentThread().getName());
+                    }
+                }
+            }, "async-post-runner-" + id).start();
+        }
+
+        /**
+         * Long-running async get request that times out.
+         *
+         * @param asyncResponse async response.
+         */
+        @GET
+        @Path("timeout")
+        public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
+            LOGGER.info("Async long-running get with timeout called on thread " + Thread.currentThread().getName());
+            asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+                @Override
+                public void handleTimeout(final AsyncResponse asyncResponse) {
+                    asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+                            .entity("Operation time out.").build());
+                }
+            });
+            asyncResponse.setTimeout(1, TimeUnit.SECONDS);
+
+            new Thread(new Runnable() {
+
+                @Override
+                public void run() {
+                    final String result = veryExpensiveOperation();
+                    asyncResponse.resume(result);
+                }
+
+                private String veryExpensiveOperation() {
+                    // very expensive operation that typically finishes within 1 second but can take up to 5 seconds,
+                    // simulated using sleep()
+                    try {
+                        Thread.sleep(5 * OPERATION_DURATION);
+                        return "DONE";
+                    } catch (final InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                        return "INTERRUPTED";
+                    } finally {
+                        LOGGER.info("Async long-running get with timeout finished on thread " + Thread.currentThread().getName());
+                    }
+                }
+            }).start();
+        }
+
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(AsyncResource.class, ThreadPoolResource.class)
+                .register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+        config.connectorProvider(new HttpUrlConnectorProvider());
+    }
+
+    /**
+     * Test asynchronous POST.
+     *
+     * Send 3 async POST requests and wait to receive the responses. Check the response content and
+     * assert that the operation did not take more than twice as long as a single long operation duration
+     * (this ensures async request execution).
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testAsyncPost() throws Exception {
+        final long tic = System.currentTimeMillis();
+
+        // Submit requests asynchronously.
+        final Future<Response> rf1 = target(PATH).request().async().post(Entity.text("1"));
+        final Future<Response> rf2 = target(PATH).request().async().post(Entity.text("2"));
+        final Future<Response> rf3 = target(PATH).request().async().post(Entity.text("3"));
+        // get() waits for the response
+        final String r1 = rf1.get().readEntity(String.class);
+        final String r2 = rf2.get().readEntity(String.class);
+        final String r3 = rf3.get().readEntity(String.class);
+
+        final long toc = System.currentTimeMillis();
+
+        assertEquals("DONE-1", r1);
+        assertEquals("DONE-2", r2);
+        assertEquals("DONE-3", r3);
+
+        assertThat("Async processing took too long.", toc - tic, Matchers.lessThan(3 * AsyncResource.OPERATION_DURATION));
+    }
+
+    /**
+     * Test accessing an operation that times out on the server.
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testAsyncGetWithTimeout() throws Exception {
+        final Future<Response> responseFuture = target(PATH).path("timeout").request().async().get();
+        // Request is being processed asynchronously.
+        final Response response = responseFuture.get();
+
+        // get() waits for the response
+        assertEquals(503, response.getStatus());
+        assertEquals("Operation time out.", response.readEntity(String.class));
+    }
+
+    @Path("/threadpool")
+    public static class ThreadPoolResource {
+
+        @GET
+        public String get() {
+            sleep();
+            return "GET";
+        }
+
+        private void sleep() {
+            try {
+                Thread.sleep(100);
+            } catch (final InterruptedException ex) {
+                // NOOP.
+            }
+        }
+    }
+
+    @Test
+    @Ignore("Unstable test.")
+    public void testClientThreadPool() throws Exception {
+        final AsyncInvoker invoker = ClientBuilder
+                .newClient(new ClientConfig().property(ClientProperties.ASYNC_THREADPOOL_SIZE, 9))
+                .target(getBaseUri())
+                .path("threadpool")
+                .request()
+                .async();
+
+        final CountDownLatch latch = new CountDownLatch(100);
+        final int threadCount = Thread.activeCount();
+
+        final List<Thread> threads = new ArrayList<Thread>(20);
+        for (int i = 0; i < 20; i++) {
+            threads.add(new Thread(new Runnable() {
+                @Override
+                public void run() throws RuntimeException {
+                    for (int i = 0; i < 5; i++) {
+                        try {
+                            assertThat(invoker.get().get().readEntity(String.class), equalTo("GET"));
+                            assertThat(Thread.activeCount() - threadCount - 20, lessThanOrEqualTo(10));
+                            latch.countDown();
+                        } catch (final InterruptedException e) {
+                            fail();
+                        } catch (final ExecutionException e) {
+                            fail();
+                        }
+                    }
+                }
+            }));
+        }
+        for (final Thread thread : threads) {
+            thread.start();
+        }
+
+        assertTrue(latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
+    }
+}
diff --git a/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-client b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-client
new file mode 100644
index 0000000..d016fd2
--- /dev/null
+++ b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-client
Binary files differ
diff --git a/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-example_com-server b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-example_com-server
new file mode 100644
index 0000000..66f6e9a
--- /dev/null
+++ b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-example_com-server
Binary files differ
diff --git a/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-localhost-server b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-localhost-server
new file mode 100644
index 0000000..b86a87d
--- /dev/null
+++ b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-localhost-server
Binary files differ
diff --git a/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-example_com-client b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-example_com-client
new file mode 100644
index 0000000..0cfac86
--- /dev/null
+++ b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-example_com-client
Binary files differ
diff --git a/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-localhost-client b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-localhost-client
new file mode 100644
index 0000000..29d6102
--- /dev/null
+++ b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-localhost-client
Binary files differ
diff --git a/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-server b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-server
new file mode 100644
index 0000000..9b26ce4
--- /dev/null
+++ b/tests/e2e-client/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-server
Binary files differ
diff --git a/tests/e2e-core-common/pom.xml b/tests/e2e-core-common/pom.xml
new file mode 100644
index 0000000..ea70000
--- /dev/null
+++ b/tests/e2e-core-common/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-core-common</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e-core-common</name>
+
+    <description>Jersey E2E Core-Common tests</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>xdk</id>
+            <properties>
+                <!-- do not use security manager for xdk -->
+                <surefire.security.argline />
+            </properties>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java
new file mode 100644
index 0000000..185202c
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/TestRuntimeDelegate.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, 2018 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.common;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.Variant;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.internal.AbstractRuntimeDelegate;
+import org.glassfish.jersey.message.internal.MessagingBinders;
+
+import org.junit.Assert;
+
+/**
+ * Test runtime delegate.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class TestRuntimeDelegate extends AbstractRuntimeDelegate {
+
+    public TestRuntimeDelegate() {
+        super(new MessagingBinders.HeaderDelegateProviders().getHeaderDelegateProviders());
+    }
+
+    @Override
+    public <T> T createEndpoint(Application application, Class<T> endpointType)
+            throws IllegalArgumentException, UnsupportedOperationException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public void testMediaType() {
+        MediaType m = new MediaType("text", "plain");
+        Assert.assertNotNull(m);
+    }
+
+    public void testUriBuilder() {
+        UriBuilder ub = RuntimeDelegate.getInstance().createUriBuilder();
+        Assert.assertNotNull(ub);
+    }
+
+    public void testResponseBuilder() {
+        Response.ResponseBuilder rb = RuntimeDelegate.getInstance().createResponseBuilder();
+        Assert.assertNotNull(rb);
+    }
+
+    public void testVariantListBuilder() {
+        Variant.VariantListBuilder vlb = RuntimeDelegate.getInstance().createVariantListBuilder();
+        Assert.assertNotNull(vlb);
+    }
+
+    public void testLinkBuilder() {
+        final Link.Builder linkBuilder = RuntimeDelegate.getInstance().createLinkBuilder();
+        Assert.assertNotNull(linkBuilder);
+    }
+
+    public void testWebApplicationException() {
+        WebApplicationException wae = new WebApplicationException();
+        Assert.assertNotNull(wae);
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/ServiceFinderBinderTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/ServiceFinderBinderTest.java
new file mode 100644
index 0000000..1195233
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/ServiceFinderBinderTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.config;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.RuntimeType;
+
+import org.glassfish.jersey.internal.ServiceFinderBinder;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.Providers;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Service finder injection binder unit test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ServiceFinderBinderTest {
+
+    private static InjectionManager injectionManager;
+
+    public ServiceFinderBinderTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        AbstractBinder binder = new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(TestServiceB.class).to(TestContract.class);
+                bind(TestServiceD.class).to(TestContract.class);
+                install(new ServiceFinderBinder<>(TestContract.class, null, RuntimeType.SERVER));
+            }
+        };
+        injectionManager = Injections.createInjectionManager(binder);
+        injectionManager.completeRegistration();
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Test
+    public void testConfigure() {
+        final Set<TestContract> providers = Providers.getProviders(injectionManager, TestContract.class);
+        assertEquals(4, providers.size());
+
+        final Collection<String> providerNames =
+                providers.stream()
+                         .map(TestContract::name)
+                         .collect(Collectors.toList());
+
+        assertTrue(providerNames.contains(TestServiceA.class.getName()));
+        assertTrue(providerNames.contains(TestServiceB.class.getName()));
+        assertTrue(providerNames.contains(TestServiceC.class.getName()));
+        assertTrue(providerNames.contains(TestServiceD.class.getName()));
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestContract.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestContract.java
new file mode 100644
index 0000000..3aa1482
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestContract.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.config;
+
+/**
+ * Test contract for {@link org.glassfish.jersey.internal.ServiceFinderBinder} test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public interface TestContract {
+    /**
+     * Get service name.
+     *
+     * @return service name.
+     */
+    public String name();
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceA.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceA.java
new file mode 100644
index 0000000..046d639
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceA.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.config;
+
+/**
+ * Test contract implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class TestServiceA implements TestContract {
+
+    @Override
+    public String name() {
+        return getClass().getName();
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceB.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceB.java
new file mode 100644
index 0000000..cd957b9
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceB.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.config;
+
+/**
+ * Test contract implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class TestServiceB implements TestContract {
+
+    @Override
+    public String name() {
+        return getClass().getName();
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceC.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceC.java
new file mode 100644
index 0000000..6409a38
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceC.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.config;
+
+/**
+ * Test contract implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class TestServiceC implements TestContract {
+
+    @Override
+    public String name() {
+        return getClass().getName();
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceD.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceD.java
new file mode 100644
index 0000000..8a73c53
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/config/TestServiceD.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.config;
+
+/**
+ * Test contract implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class TestServiceD implements TestContract {
+
+    @Override
+    public String name() {
+        return getClass().getName();
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ContextResolverFactoryTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ContextResolverFactoryTest.java
new file mode 100644
index 0000000..7c00edd
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ContextResolverFactoryTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.internal;
+
+import java.util.Collections;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.internal.BootstrapBag;
+import org.glassfish.jersey.internal.ContextResolverFactory;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.ProviderBinder;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Context resolvers factory unit test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ContextResolverFactoryTest {
+
+    @Provider
+    private static class CustomStringResolver implements ContextResolver<String> {
+
+        public static final String VALUE = "foof";
+
+        @Override
+        public String getContext(Class<?> type) {
+            return VALUE;
+        }
+    }
+
+    @Provider
+    @Produces("application/*")
+    private static class CustomIntegerResolverA implements ContextResolver<Integer> {
+
+        public static final int VALUE = 1001;
+
+        @Override
+        public Integer getContext(Class<?> type) {
+            return VALUE;
+        }
+    }
+
+    @Provider
+    @Produces("application/json")
+    private static class CustomIntegerResolverB implements ContextResolver<Integer> {
+
+        public static final int VALUE = 2002;
+
+        @Override
+        public Integer getContext(Class<?> type) {
+            return VALUE;
+        }
+    }
+
+    @Provider
+    @Produces("application/json")
+    private static class CustomIntegerResolverC implements ContextResolver<Integer> {
+
+        public static final int VALUE = 3003;
+
+        @Override
+        public Integer getContext(Class<?> type) {
+            return VALUE;
+        }
+    }
+
+    private static class Binder extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            bind(CustomStringResolver.class).to(ContextResolver.class);
+            bind(CustomIntegerResolverA.class).to(ContextResolver.class);
+            bind(CustomIntegerResolverB.class).to(ContextResolver.class);
+        }
+    }
+
+    private ContextResolverFactory crf;
+
+    public ContextResolverFactoryTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @Before
+    public void setUp() {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindClasses(Collections.singleton(CustomIntegerResolverC.class));
+        injectionManager.register(new Binder());
+
+        BootstrapBag bootstrapBag = new BootstrapBag();
+        ContextResolverFactory.ContextResolversConfigurator configurator =
+                new ContextResolverFactory.ContextResolversConfigurator();
+        configurator.init(injectionManager, bootstrapBag);
+        injectionManager.completeRegistration();
+        configurator.postInit(injectionManager, bootstrapBag);
+
+        crf = injectionManager.getInstance(ContextResolverFactory.class);
+    }
+
+    @Test
+    public void testResolve() {
+        assertEquals(CustomStringResolver.VALUE, crf.resolve(String.class, MediaType.WILDCARD_TYPE).getContext(String.class));
+        assertEquals(CustomStringResolver.VALUE, crf.resolve(String.class, MediaType.TEXT_PLAIN_TYPE).getContext(String.class));
+
+        assertEquals(CustomIntegerResolverA.VALUE,
+                crf.resolve(Integer.class, MediaType.APPLICATION_XML_TYPE).getContext(Integer.class));
+        assertEquals(CustomIntegerResolverA.VALUE,
+                crf.resolve(Integer.class, MediaType.valueOf("application/*")).getContext(Integer.class));
+
+        // Test that resolver "B" is shadowed by a custom resolver "C"
+        assertEquals(CustomIntegerResolverC.VALUE,
+                crf.resolve(Integer.class, MediaType.APPLICATION_JSON_TYPE).getContext(Integer.class));
+
+        // Test that there is no matching provider
+        assertNull(crf.resolve(Integer.class, MediaType.TEXT_PLAIN_TYPE));
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ExceptionMapperFactoryTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ExceptionMapperFactoryTest.java
new file mode 100644
index 0000000..24b4ffa
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ExceptionMapperFactoryTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2015, 2018 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.common.internal;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.ExceptionMapperFactory;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.spi.ExtendedExceptionMapper;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit test of {@link ExceptionMapperFactory}.
+ */
+public class ExceptionMapperFactoryTest {
+
+    private static class ExtendedExceptionMappers extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            bind(IllegalArgumentExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
+            bind(IllegalStateExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
+        }
+
+    }
+
+    private static class AllMappers extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            bind(IllegalArgumentExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
+            bind(IllegalStateExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
+            bind(RuntimeExceptionMapper.class).to(ExceptionMapper.class).in(Singleton.class);
+        }
+
+    }
+
+    /**
+     * Test spec:
+     * <p/>
+     * setup:<br/>
+     * - have two extended exception mappers, order matters<br/>
+     * - both using the same generic type (RuntimeException)<br/>
+     * - first mapper return isMappable true only to IllegalArgumentException<br/>
+     * - second mapper return isMappable true only to IllegalStateException<br/>
+     * <br/>
+     * when:<br/>
+     * - {@link ExceptionMapperFactory#findMapping(Throwable)} with IllegalArgumentException instance<br/>
+     * <br/>
+     * then:<br/>
+     * - exception mapper factory returns IllegalArgumentExceptionMapper<br/>
+     * <p/>
+     * why:<br/>
+     * - IllegalArgumentException has the same distance (1) for both exception mappers generic type (RuntimeException),
+     * but IllegalArgumentException's isMappable return true, so it is the winner
+     *
+     * @throws Exception unexpected - if anything goes wrong, the test fails
+     */
+    @Test
+    public void testFindMappingExtendedExceptions() throws Exception {
+        final InjectionManager injectionManager = Injections.createInjectionManager(new ExtendedExceptionMappers());
+        injectionManager.completeRegistration();
+        final ExceptionMapperFactory mapperFactory = new ExceptionMapperFactory(injectionManager);
+
+        final ExceptionMapper mapper = mapperFactory.findMapping(new IllegalArgumentException());
+
+        Assert.assertTrue("IllegalArgumentExceptionMapper should be returned",
+                mapper instanceof IllegalArgumentExceptionMapper);
+    }
+
+    /**
+     * Test spec:
+     * <p/>
+     * setup:<br/>
+     * - have 3 exception mappers, order matters<br/>
+     * - first is *not* extended mapper typed to RuntimeException
+     * - second and third are extended mappers type to RuntimeException
+     * <br/>
+     * when:<br/>
+     * - {@link ExceptionMapperFactory#findMapping(Throwable)} invoked with RuntimeException instance<br/>
+     * then: <br/>
+     * - exception mapper factory returns RuntimeExceptionMapper<br/>
+     * <p/>
+     * why:<br/>
+     * - RuntimeException mapper has distance 0 for RuntimeException, it is not extended mapper, so it will be chosen
+     * immediately, cause there is no better option possible
+     *
+     * @throws Exception unexpected - if anything goes wrong, the test fails
+     */
+    @Test
+    public void testFindMapping() throws Exception {
+        final InjectionManager injectionManager = Injections.createInjectionManager(new AllMappers());
+        injectionManager.completeRegistration();
+        final ExceptionMapperFactory mapperFactory = new ExceptionMapperFactory(injectionManager);
+
+        final ExceptionMapper<RuntimeException> mapper = mapperFactory.findMapping(new RuntimeException());
+
+        Assert.assertTrue("RuntimeExceptionMapper should be returned", mapper instanceof RuntimeExceptionMapper);
+    }
+
+    /**
+     * Test spec: <br/>
+     * <p/>
+     * setup:<br/>
+     * - have 2 extended mappers, order matters<br/>
+     * - first mapper return isMappable true only to IllegalArgumentException<br/>
+     * - second mapper return isMappable true only to IllegalStateException<br/>
+     * <br/>
+     * when:<br/>
+     * - {@link ExceptionMapperFactory#find(Class)} invoked with IllegalArgumentException.class<br/>
+     * then:<br/>
+     * - exception mapper factory returns IllegalArgumentExceptionMapper<br/>
+     * <p/>
+     * why:<br/>
+     * - both exception mappers have distance 1 to IllegalArgumentException, we don't have instance of the
+     * IllegalArgumentException, so the isMappable check is not used and both are accepted, the later accepted is
+     * the winner
+     *
+     * @throws Exception unexpected - if anything goes wrong, the test fails
+     */
+    @Test
+    public void testFindExtendedExceptions() throws Exception {
+        final InjectionManager injectionManager = Injections.createInjectionManager(new ExtendedExceptionMappers());
+        injectionManager.completeRegistration();
+        final ExceptionMapperFactory mapperFactory = new ExceptionMapperFactory(injectionManager);
+
+        final ExceptionMapper mapper = mapperFactory.find(IllegalArgumentException.class);
+
+        Assert.assertTrue("IllegalStateExceptionMapper should be returned",
+                mapper instanceof IllegalStateExceptionMapper);
+    }
+
+    /**
+     * Extended Exception Mapper which has RuntimeException as generic type and isMappable returns true if the
+     * exception is instance of IllegalArgumentException.
+     */
+    private static class IllegalArgumentExceptionMapper implements ExtendedExceptionMapper<RuntimeException> {
+
+        @Override
+        public boolean isMappable(final RuntimeException exception) {
+            return exception instanceof IllegalArgumentException;
+        }
+
+        @Override
+        public Response toResponse(final RuntimeException exception) {
+            return Response
+                    .status(Response.Status.BAD_REQUEST)
+                    .build();
+        }
+
+    }
+
+    /**
+     * Extended Exception Mapper which has RuntimeException as generic type and isMappable returns true if the
+     * exception is instance of IllegalStateException.
+     */
+    private static class IllegalStateExceptionMapper implements ExtendedExceptionMapper<RuntimeException> {
+
+        @Override
+        public boolean isMappable(final RuntimeException exception) {
+            return exception instanceof IllegalStateException;
+        }
+
+        @Override
+        public Response toResponse(final RuntimeException exception) {
+            return Response
+                    .status(Response.Status.SERVICE_UNAVAILABLE)
+                    .build();
+        }
+
+    }
+
+    /**
+     * Exception Mapper which has RuntimeException as generic type.
+     */
+    private static class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
+
+        @Override
+        public Response toResponse(final RuntimeException exception) {
+            return Response
+                    .status(Response.Status.INTERNAL_SERVER_ERROR)
+                    .build();
+        }
+
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/JaxrsProvidersTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/JaxrsProvidersTest.java
new file mode 100644
index 0000000..beb6221
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/JaxrsProvidersTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.internal;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Providers;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.internal.BootstrapBag;
+import org.glassfish.jersey.internal.BootstrapConfigurator;
+import org.glassfish.jersey.internal.ContextResolverFactory;
+import org.glassfish.jersey.internal.ExceptionMapperFactory;
+import org.glassfish.jersey.internal.JaxrsProviders;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.message.internal.MessageBodyFactory;
+import org.glassfish.jersey.message.internal.MessagingBinders;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+import org.glassfish.jersey.process.internal.RequestScope;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Test;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+/**®
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class JaxrsProvidersTest {
+
+    private static class Binder extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            bind(new ContextResolver<String>() {
+                @Override
+                public String getContext(Class<?> type) {
+                    throw new UnsupportedOperationException("Not supported yet.");
+                }
+            }).to(new GenericType<ContextResolver<String>>() {
+            });
+
+            bind(new CommonConfig(RuntimeType.SERVER, ComponentBag.EXCLUDE_EMPTY)).to(Configuration.class);
+        }
+    }
+
+    public JaxrsProvidersTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @Test
+    public void testProviders() throws Exception {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        injectionManager.register(new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER));
+        injectionManager.register(new Binder());
+
+        BootstrapBag bootstrapBag = new BootstrapBag();
+        List<BootstrapConfigurator> bootstrapConfigurators = Arrays.asList(
+                new RequestScope.RequestScopeConfigurator(),
+                new TestConfigConfigurator(),
+                new ContextResolverFactory.ContextResolversConfigurator(),
+                new MessageBodyFactory.MessageBodyWorkersConfigurator(),
+                new ExceptionMapperFactory.ExceptionMappersConfigurator(),
+                new JaxrsProviders.ProvidersConfigurator());
+        injectionManager.register(new TestBinder());
+
+        TestBinder.initProviders(injectionManager);
+        bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));
+        injectionManager.completeRegistration();
+        bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, bootstrapBag));
+
+        RequestScope scope = bootstrapBag.getRequestScope();
+
+        scope.runInScope((Callable<Object>) () -> {
+            Providers instance = injectionManager.getInstance(Providers.class);
+
+            assertNotNull(instance);
+            assertSame(JaxrsProviders.class, instance.getClass());
+
+            assertNotNull(instance.getExceptionMapper(Throwable.class));
+            assertNotNull(instance.getMessageBodyReader(String.class, String.class, new Annotation[0],
+                    MediaType.TEXT_PLAIN_TYPE));
+            assertNotNull(instance.getMessageBodyWriter(String.class, String.class, new Annotation[0],
+                    MediaType.TEXT_PLAIN_TYPE));
+            assertNotNull(instance.getContextResolver(String.class, MediaType.TEXT_PLAIN_TYPE));
+            return null;
+        });
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/LocalizationMessagesTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/LocalizationMessagesTest.java
new file mode 100644
index 0000000..22c679f
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/LocalizationMessagesTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 2018 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.common.internal;
+
+
+import org.glassfish.jersey.internal.LocalizationMessages;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link org.glassfish.jersey.internal.LocalizationMessages}.
+ * @author Miroslav Fuksa
+ */
+public class LocalizationMessagesTest {
+
+    @Test
+    public void test() {
+        Assert.assertTrue(LocalizationMessages.COMPONENT_CONTRACTS_EMPTY_OR_NULL("TYPE")
+                .contains("Attempt to register component of type TYPE"));
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ProviderBinderTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ProviderBinderTest.java
new file mode 100644
index 0000000..8e60873
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/ProviderBinderTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.Binder;
+import org.glassfish.jersey.internal.inject.CompositeBinder;
+import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.ProviderBinder;
+import org.glassfish.jersey.internal.inject.Providers;
+import org.glassfish.jersey.message.internal.MessagingBinders;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ServiceProviders unit test.
+ *
+ * @author Santiago Pericas-Geertsen (santiago.pericasgeertsen at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class ProviderBinderTest {
+
+    private static class MyProvider implements MessageBodyReader, MessageBodyWriter {
+
+        @Override
+        public boolean isReadable(Class type, Type genericType, Annotation[] annotations,
+                                  MediaType mediaType) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                               MultivaluedMap httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public boolean isWriteable(Class type, Type genericType, Annotation[] annotations,
+                                   MediaType mediaType) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public long getSize(Object t, Class type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public void writeTo(Object t, Class type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    private static Binder initBinders(Binder... binders) {
+        List<Binder> binderList = Arrays.stream(binders).collect(Collectors.toList());
+        binderList.add(new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER));
+        return CompositeBinder.wrap(binderList);
+    }
+
+    public ProviderBinderTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @Test
+    public void testServicesNotEmpty() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        injectionManager.completeRegistration();
+        Set<MessageBodyReader> providers = Providers.getProviders(injectionManager, MessageBodyReader.class);
+        assertTrue(providers.size() > 0);
+    }
+
+    @Test
+    public void testServicesMbr() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        injectionManager.completeRegistration();
+        Set<MessageBodyReader> providers = Providers.getProviders(injectionManager, MessageBodyReader.class);
+        assertTrue(providers.size() > 0);
+    }
+
+    @Test
+    public void testServicesMbw() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        injectionManager.completeRegistration();
+        Set<MessageBodyWriter> providers = Providers.getProviders(injectionManager, MessageBodyWriter.class);
+        assertTrue(providers.size() > 0);
+    }
+
+    @Test
+    public void testProvidersMbr() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindClasses(Collections.singleton(MyProvider.class));
+
+        injectionManager.completeRegistration();
+        Set<MessageBodyReader> providers = Providers.getCustomProviders(injectionManager, MessageBodyReader.class);
+        assertEquals(1, instancesOfType(MyProvider.class, providers).size());
+    }
+
+    @Test
+    public void testProvidersMbw() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindClasses(Collections.singleton(MyProvider.class));
+
+        injectionManager.completeRegistration();
+        Set<MessageBodyWriter> providers = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class);
+        final Collection<MyProvider> myProviders = instancesOfType(MyProvider.class, providers);
+        assertEquals(1, myProviders.size());
+    }
+
+    @Test
+    public void testProvidersMbrInstance() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindInstances(Collections.singleton(new MyProvider()));
+
+        injectionManager.completeRegistration();
+        Set<MessageBodyReader> providers = Providers.getCustomProviders(injectionManager, MessageBodyReader.class);
+        assertEquals(1, instancesOfType(MyProvider.class, providers).size());
+    }
+
+    @Test
+    public void testProvidersMbwInstance() {
+        InjectionManager injectionManager = Injections.createInjectionManager(initBinders());
+        ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindInstances(Collections.singleton(new MyProvider()));
+
+        injectionManager.completeRegistration();
+        Set<MessageBodyWriter> providers = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class);
+        assertEquals(instancesOfType(MyProvider.class, providers).size(), 1);
+    }
+
+    private <T> Collection<T> instancesOfType(final Class<T> c, Collection<?> collection) {
+
+        return collection.stream()
+                .filter((java.util.function.Predicate<Object>) o -> o.getClass() == c)
+                .map((java.util.function.Function<Object, T>) c::cast)
+                .collect(Collectors.toList());
+    }
+
+
+    @Test
+    public void testCustomRegistration() {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+
+        ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindClasses(Child.class);
+        providerBinder.bindClasses(NotFilterChild.class);
+        injectionManager.completeRegistration();
+
+        ContainerRequestFilter requestFilter = getRequestFilter(injectionManager);
+        ContainerRequestFilter requestFilter2 = getRequestFilter(injectionManager);
+        assertEquals(requestFilter, requestFilter2);
+
+
+        ContainerResponseFilter responseFilter = getResponseFilter(injectionManager);
+        ContainerResponseFilter responseFilter2 = getResponseFilter(injectionManager);
+        assertTrue(responseFilter == responseFilter2);
+
+        assertTrue(responseFilter == requestFilter);
+
+        // only one filter should be registered
+        Collection<ContainerResponseFilter> filters =
+                Providers.getCustomProviders(injectionManager, ContainerResponseFilter.class);
+        assertEquals(1, filters.size());
+
+        Child child = injectionManager.getInstance(Child.class);
+        Child child2 = injectionManager.getInstance(Child.class);
+
+        assertTrue(child != responseFilter);
+
+        assertTrue(child == child2);
+    }
+
+    private ContainerResponseFilter getResponseFilter(InjectionManager injectionManager) {
+        ContainerResponseFilter responseFilter =
+                injectionManager.getInstance(ContainerResponseFilter.class, CustomAnnotationLiteral.INSTANCE);
+        assertEquals(Child.class, responseFilter.getClass());
+        return responseFilter;
+    }
+
+    private ContainerRequestFilter getRequestFilter(InjectionManager injectionManager) {
+        ContainerRequestFilter requestFilter =
+                injectionManager.getInstance(ContainerRequestFilter.class, CustomAnnotationLiteral.INSTANCE);
+        assertEquals(Child.class, requestFilter.getClass());
+        return requestFilter;
+    }
+
+    interface ParentInterface {
+    }
+
+    interface ChildInterface extends ChildSuperInterface {
+    }
+
+
+    interface SecondChildInterface {
+    }
+
+    interface ChildSuperInterface extends ContainerResponseFilter {
+    }
+
+    @Singleton
+    public static class Parent implements ParentInterface, ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+        }
+    }
+
+    @Singleton
+    public static class Child extends Parent implements ChildInterface, SecondChildInterface {
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        }
+    }
+
+    private static class NotFilterChild implements ParentInterface {
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/TestBinder.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/TestBinder.java
new file mode 100644
index 0000000..b166748
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/TestBinder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.internal;
+
+import java.util.Collections;
+
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.ProviderBinder;
+import org.glassfish.jersey.message.internal.MessagingBinders;
+
+/**
+ * Binder for testing purposes.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class TestBinder extends AbstractBinder {
+
+    public static void initProviders(final InjectionManager injectionManager) {
+        initProviders(injectionManager, Collections.emptySet(), Collections.emptySet());
+    }
+
+    public static void initProviders(final InjectionManager injectionManager,
+                                     final Iterable<Class<?>> providerClasses,
+                                     final Iterable<Object> providerInstances) {
+        final ProviderBinder providerBinder = new ProviderBinder(injectionManager);
+        providerBinder.bindClasses(providerClasses);
+        providerBinder.bindInstances(providerInstances);
+    }
+
+    @Override
+    protected void configure() {
+        install(new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER));
+
+        bind(new ExceptionMapper<Throwable>() {
+            @Override
+            public Response toResponse(Throwable exception) {
+                if (exception instanceof NumberFormatException) {
+                    return Response.ok(-1).build();
+                }
+
+                throw new RuntimeException(exception);
+            }
+        }).to(ExceptionMapper.class);
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/TestConfigConfigurator.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/TestConfigConfigurator.java
new file mode 100644
index 0000000..6617ca9
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/TestConfigConfigurator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, 2018 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.common.internal;
+
+import javax.ws.rs.RuntimeType;
+
+import org.glassfish.jersey.internal.BootstrapBag;
+import org.glassfish.jersey.internal.BootstrapConfigurator;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+
+/**
+ * Configurator which initializes configuration.
+ *
+ * @author Petr Bouda
+ */
+public class TestConfigConfigurator implements BootstrapConfigurator {
+
+    @Override
+    public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
+        bootstrapBag.setConfiguration(new CommonConfig(RuntimeType.SERVER, ComponentBag.EXCLUDE_EMPTY));
+    }
+
+    @Override
+    public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/inject/ReferencingFactoryTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/inject/ReferencingFactoryTest.java
new file mode 100644
index 0000000..6aaf923
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/internal/inject/ReferencingFactoryTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.internal.inject;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.core.GenericType;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.ReferencingFactory;
+import org.glassfish.jersey.internal.util.collection.Ref;
+
+import org.junit.Test;
+import static org.junit.Assert.assertSame;
+
+/**
+ * Referencing factory test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ReferencingFactoryTest extends AbstractBinder {
+
+    private static class Foo {
+        final int value;
+
+        private Foo(int value) {
+            this.value = value;
+        }
+    }
+
+    private static class ValueInjected {
+
+        @Inject
+        Foo foo;
+        @Inject
+        List<Integer> integers;
+        @Inject
+        List<String> strings;
+    }
+
+    private static class RefInjected {
+
+        @Inject
+        Ref<Foo> foo;
+        @Inject
+        Ref<List<Integer>> integers;
+        @Inject
+        Ref<List<String>> strings;
+    }
+
+    //
+    private Foo expectedFoo = null;
+    private List<Integer> expectedIntegers = null;
+    private List<String> expectedStrings = new LinkedList<String>();
+
+    private static final class FooReferencingFactory extends ReferencingFactory<Foo> {
+        @Inject
+        public FooReferencingFactory(Provider<Ref<Foo>> referenceFactory) {
+            super(referenceFactory);
+        }
+    }
+
+    private static final class ListOfIntegerReferencingFactory extends ReferencingFactory<List<Integer>> {
+        @Inject
+        public ListOfIntegerReferencingFactory(Provider<Ref<List<Integer>>> referenceFactory) {
+            super(referenceFactory);
+        }
+    }
+
+    private static final class ListOfStringReferencingFactory extends ReferencingFactory<List<String>> {
+        @Inject
+        public ListOfStringReferencingFactory(Provider<Ref<List<String>>> referenceFactory) {
+            super(referenceFactory);
+        }
+    }
+
+    @Override
+    protected void configure() {
+        bindFactory(FooReferencingFactory.class).to(Foo.class);
+        bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType<Ref<Foo>>() {}).in(Singleton.class);
+
+        bindFactory(ListOfIntegerReferencingFactory.class).to(new GenericType<List<Integer>>() {});
+        bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType<Ref<List<Integer>>>() {
+        }).in(Singleton.class);
+
+        bindFactory(ListOfStringReferencingFactory.class).to(new GenericType<List<String>>() {});
+        bindFactory(ReferencingFactory.referenceFactory(expectedStrings)).to(new GenericType<Ref<List<String>>>() {
+        }).in(Singleton.class);
+    }
+
+    /**
+     * Referenced binding test.
+     */
+    @Test
+    public void testReferencedBinding() {
+        InjectionManager injectionManager = Injections.createInjectionManager(this);
+        injectionManager.completeRegistration();
+
+        RefInjected refValues = injectionManager.createAndInitialize(RefInjected.class);
+        expectedFoo = new Foo(10);
+        refValues.foo.set(expectedFoo);
+        expectedIntegers = new LinkedList<Integer>();
+        refValues.integers.set(expectedIntegers);
+        expectedStrings = new ArrayList<String>();
+        refValues.strings.set(expectedStrings);
+
+        ValueInjected updatedValues = injectionManager.createAndInitialize(ValueInjected.class);
+        assertSame(expectedFoo, updatedValues.foo);
+        assertSame(expectedIntegers, updatedValues.integers);
+        assertSame(expectedStrings, updatedValues.strings);
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/AcceptableMediaTypeStringRepresentationTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/AcceptableMediaTypeStringRepresentationTest.java
new file mode 100644
index 0000000..6714257
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/AcceptableMediaTypeStringRepresentationTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2018 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.common.message.internal;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.glassfish.jersey.message.internal.AcceptableMediaType;
+import org.glassfish.jersey.message.internal.MediaTypeProvider;
+import org.glassfish.jersey.message.internal.Quality;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+
+/**
+ * Acceptable media type unit tests.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class AcceptableMediaTypeStringRepresentationTest {
+    @Parameterized.Parameters
+    // expected result, acceptable media type
+    public static List<Object[]> getParameters() {
+        final Map<String, String> emptyParams = new HashMap<String, String>();
+        final Map<String, String> params = new HashMap<String, String>();
+        params.put("myParam", "myValue");
+
+        return Arrays.asList(new Object[][]{
+                {"*/*", new AcceptableMediaType("*", "*")},
+                {"*/*", new AcceptableMediaType("*", "*", Quality.DEFAULT, emptyParams)},
+                {"*/*;q=0.75", new AcceptableMediaType("*", "*", 750, emptyParams)},
+                {"text/html", new AcceptableMediaType("text", "html", Quality.DEFAULT, null)},
+                {"text/html;q=0.5", new AcceptableMediaType("text", "html", 500, emptyParams)},
+                {"image/*;myparam=myValue;q=0.8", new AcceptableMediaType("image", "*", 800, params)},
+        });
+    }
+
+    private final String expectedValue;
+    private final AcceptableMediaType testedType;
+
+    public AcceptableMediaTypeStringRepresentationTest(final String expectedValue,
+                                                       final AcceptableMediaType testedType) {
+        this.expectedValue = expectedValue;
+        this.testedType = testedType;
+    }
+
+    @Test
+    public void testStringRepresentation() {
+        final MediaTypeProvider provider = new MediaTypeProvider();
+        Assert.assertEquals(expectedValue, testedType.toString());
+        provider.fromString(testedType.toString());
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/AcceptableMediaTypeTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/AcceptableMediaTypeTest.java
new file mode 100644
index 0000000..198a905
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/AcceptableMediaTypeTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.message.internal;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.AcceptableMediaType;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Acceptable media type unit tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class AcceptableMediaTypeTest {
+    @Parameterized.Parameters
+    // expected result, media type, acceptable media type
+    public static List<Object[]> testBeds() {
+        return Arrays.asList(new Object[][]{
+                {Boolean.TRUE, MediaType.APPLICATION_JSON_TYPE, new AcceptableMediaType("application", "json")},
+                {Boolean.TRUE, MediaType.APPLICATION_JSON_TYPE, new AcceptableMediaType("application", "json", 1000, null)},
+                {Boolean.FALSE, MediaType.APPLICATION_JSON_TYPE, new AcceptableMediaType("application", "json", 500, null)},
+                {Boolean.FALSE, MediaType.APPLICATION_JSON_TYPE, new AcceptableMediaType("application", "xml")}
+        });
+    }
+
+    private final boolean expectEquality;
+    private final MediaType mediaType;
+    private final AcceptableMediaType acceptableMediaType;
+
+    public AcceptableMediaTypeTest(boolean expectEquality, MediaType mediaType, AcceptableMediaType acceptableMediaType) {
+        this.expectEquality = expectEquality;
+        this.mediaType = mediaType;
+        this.acceptableMediaType = acceptableMediaType;
+    }
+
+    @Test
+    public void testEquals() throws Exception {
+        if (expectEquality) {
+            Assert.assertEquals("Types not equal.", mediaType, acceptableMediaType);
+            Assert.assertEquals("Types not equal.", acceptableMediaType, mediaType);
+            Assert.assertEquals(
+                    String.format("Hash codes not equal for %s and %s.", mediaType.toString(), acceptableMediaType.toString()),
+                    mediaType.hashCode(), acceptableMediaType.hashCode());
+        } else {
+            Assert.assertFalse(String.format("False equality of %s and %s", mediaType.toString(), acceptableMediaType.toString()),
+                    acceptableMediaType.equals(mediaType));
+        }
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CommittingOutputStreamTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CommittingOutputStreamTest.java
new file mode 100644
index 0000000..5361275
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CommittingOutputStreamTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2013, 2018 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.common.message.internal;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.RuntimeType;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.message.internal.CommittingOutputStream;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test the {@link CommittingOutputStream}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class CommittingOutputStreamTest {
+
+    private static class Passed {
+        boolean b;
+
+        public void pass() {
+            b = true;
+        }
+    }
+
+    @Test
+    public void testExactSizeOfBuffer() throws IOException {
+        final Passed passed = new Passed();
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
+        CommittingOutputStream cos = new CommittingOutputStream();
+        setupBufferedStreamProvider(passed, baos, cos, 3);
+
+        cos.write((byte) 1);
+        cos.write((byte) 2);
+        cos.write((byte) 3);
+        checkNotYetCommitted(passed, baos, cos);
+        cos.commit();
+        check(baos, new byte[]{1, 2, 3});
+        assertTrue(passed.b);
+        cos.close();
+    }
+
+    private void checkNotYetCommitted(Passed passed, ByteArrayOutputStream baos, CommittingOutputStream cos) {
+        assertFalse(passed.b);
+        assertFalse(cos.isCommitted());
+        check(baos, null);
+    }
+
+    private void checkCommitted(Passed passed, CommittingOutputStream cos) {
+        assertTrue(passed.b);
+        assertTrue(cos.isCommitted());
+    }
+
+    private void setupBufferedStreamProvider(final Passed passed, final ByteArrayOutputStream baos, CommittingOutputStream cos,
+                                             final int expectedContentLength) {
+        cos.setStreamProvider(new OutboundMessageContext.StreamProvider() {
+            @Override
+            public OutputStream getOutputStream(int contentLength) throws IOException {
+                assertEquals(expectedContentLength, contentLength);
+                passed.pass();
+                return baos;
+            }
+        });
+        cos.enableBuffering(3);
+    }
+
+    private void setupStreamProvider(final Passed passed, final ByteArrayOutputStream baos, CommittingOutputStream cos) {
+        cos.setStreamProvider(new OutboundMessageContext.StreamProvider() {
+
+            @Override
+            public OutputStream getOutputStream(int contentLength) throws IOException {
+                passed.pass();
+                return baos;
+            }
+        });
+
+    }
+
+    @Test
+    public void testExactSizeOfBufferByClose() throws IOException {
+        final Passed passed = new Passed();
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
+        CommittingOutputStream cos = new CommittingOutputStream();
+        setupBufferedStreamProvider(passed, baos, cos, 3);
+
+        cos.write((byte) 1);
+        cos.write((byte) 2);
+        cos.write((byte) 3);
+        checkNotYetCommitted(passed, baos, cos);
+        cos.close();
+        check(baos, new byte[]{1, 2, 3});
+        assertTrue(passed.b);
+    }
+
+    @Test
+    public void testLessBytesThanLimit() throws IOException {
+        final Passed passed = new Passed();
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
+        CommittingOutputStream cos = new CommittingOutputStream();
+        setupBufferedStreamProvider(passed, baos, cos, 2);
+
+        cos.write((byte) 1);
+        cos.write((byte) 2);
+        checkNotYetCommitted(passed, baos, cos);
+
+        cos.commit();
+
+        check(baos, new byte[]{1, 2});
+        cos.close();
+        assertTrue(passed.b);
+    }
+
+    @Test
+    public void testNoBytes() throws IOException {
+        final Passed passed = new Passed();
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
+        CommittingOutputStream cos = new CommittingOutputStream();
+        setupBufferedStreamProvider(passed, baos, cos, 0);
+
+        checkNotYetCommitted(passed, baos, cos);
+
+        cos.commit();
+
+        check(baos, null);
+        cos.close();
+        assertTrue(passed.b);
+    }
+
+    @Test
+    public void testBufferOverflow() throws IOException {
+        final Passed passed = new Passed();
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
+        CommittingOutputStream cos = new CommittingOutputStream();
+        setupBufferedStreamProvider(passed, baos, cos, -1);
+
+        cos.write((byte) 1);
+        cos.write((byte) 2);
+        cos.write((byte) 3);
+        checkNotYetCommitted(passed, baos, cos);
+        cos.write((byte) 4);
+        check(baos, new byte[]{1, 2, 3, 4});
+        cos.write((byte) 5);
+        check(baos, new byte[]{1, 2, 3, 4, 5});
+
+        cos.commit();
+
+        check(baos, new byte[]{1, 2, 3, 4, 5});
+        cos.close();
+    }
+
+    @Test
+    public void testNotBufferedOS() throws IOException {
+        final Passed passed = new Passed();
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
+        CommittingOutputStream cos = new CommittingOutputStream();
+        setupStreamProvider(passed, baos, cos);
+
+        checkNotYetCommitted(passed, baos, cos);
+        cos.write((byte) 1);
+        checkCommitted(passed, cos);
+        check(baos, new byte[]{1});
+        cos.write((byte) 2);
+        checkCommitted(passed, cos);
+        cos.write((byte) 3);
+        cos.write((byte) 4);
+        check(baos, new byte[]{1, 2, 3, 4});
+        cos.write((byte) 5);
+        check(baos, new byte[]{1, 2, 3, 4, 5});
+
+        cos.commit();
+        checkCommitted(passed, cos);
+        check(baos, new byte[]{1, 2, 3, 4, 5});
+        cos.close();
+    }
+
+    private void check(ByteArrayOutputStream baos, byte... bytes) {
+        assertEquals(bytes == null ? 0 : bytes.length, baos.size());
+
+        if (bytes != null) {
+            final byte[] actualBytes = baos.toByteArray();
+            for (int i = 0; i < bytes.length; i++) {
+                assertEquals(bytes[i], actualBytes[i]);
+            }
+        }
+    }
+
+    @Test
+    public void testPropertiesWithMessageContext() throws IOException {
+        final int size = 20;
+        Map<String, Object> properties = new HashMap<>();
+        properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size);
+        final RuntimeType runtime = RuntimeType.CLIENT;
+
+        checkBufferSize(size, properties, runtime);
+    }
+
+    @Test
+    public void testPropertiesWithMessageContextVeryBigBuffer() throws IOException {
+        final int size = 200000;
+        Map<String, Object> properties = new HashMap<>();
+        properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size);
+        final RuntimeType runtime = RuntimeType.CLIENT;
+
+        checkBufferSize(size, properties, runtime);
+    }
+
+    @Test
+    public void testPropertiesWithMessageContextMissingServerSpecific() throws IOException {
+        final int size = 22;
+        Map<String, Object> properties = new HashMap<>();
+        properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size);
+        properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER + ".client", size * 2);
+        checkBufferSize(size, properties, RuntimeType.SERVER);
+    }
+
+    @Test
+    public void testPropertiesWithMessageContextMissingServerAtAll() throws IOException {
+        final int size = 22;
+        Map<String, Object> properties = new HashMap<>();
+        properties.put(PropertiesHelper.getPropertyNameForRuntime(
+                        CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, RuntimeType.CLIENT), size);
+        checkBufferSize(CommittingOutputStream.DEFAULT_BUFFER_SIZE, properties, RuntimeType.SERVER);
+        checkBufferSize(size, properties, RuntimeType.CLIENT);
+    }
+
+    @Test
+    public void testPropertiesWithMessageContextClientOverrides() throws IOException {
+        final int size = 22;
+        Map<String, Object> properties = new HashMap<>();
+        properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size);
+        properties.put(PropertiesHelper.getPropertyNameForRuntime(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER,
+                RuntimeType.CLIENT), size * 2);
+
+        checkBufferSize(size * 2, properties, RuntimeType.CLIENT);
+        checkBufferSize(size, properties, RuntimeType.SERVER);
+    }
+
+    @Test
+    public void testPropertiesWithMessageContextDefaultNoProps() throws IOException {
+        Map<String, Object> properties = new HashMap<>();
+        final RuntimeType runtime = RuntimeType.CLIENT;
+
+        checkBufferSize(CommittingOutputStream.DEFAULT_BUFFER_SIZE, properties, runtime);
+    }
+
+    private void checkBufferSize(int expectedSize, Map<String, Object> properties, RuntimeType runtime) throws IOException {
+        OutboundMessageContext outboundMessageContext = new OutboundMessageContext();
+        final Passed passed = new Passed();
+        outboundMessageContext.setStreamProvider(new OutboundMessageContext.StreamProvider() {
+            @Override
+            public OutputStream getOutputStream(int contentLength) throws IOException {
+
+                assertEquals(-1, contentLength);
+                passed.pass();
+                return null;
+            }
+        });
+        CommonConfig configuration = new CommonConfig(runtime, ComponentBag.INCLUDE_ALL);
+        configuration.setProperties(properties);
+        outboundMessageContext.enableBuffering(configuration);
+
+        final OutputStream entityStream = outboundMessageContext.getEntityStream();
+        for (int i = 1; (i < 1000000) && (!passed.b); i++) {
+            entityStream.write((byte) 65);
+            if (i >= expectedSize + 1) {
+                break;
+            } else {
+                assertFalse("committed already with byte #" + i, passed.b);
+            }
+        }
+
+        assertTrue(passed.b);
+    }
+
+
+    @Test
+    public void testEnableBuffering() {
+        CommittingOutputStream cos = new CommittingOutputStream();
+        cos.enableBuffering(500);
+    }
+
+    @Test
+    public void testEnableBufferingIllegalStateException() throws IOException {
+        CommittingOutputStream cos = new CommittingOutputStream();
+        cos.setStreamProvider(new OutboundMessageContext.StreamProvider() {
+            @Override
+            public OutputStream getOutputStream(int contentLength) throws IOException {
+                return null;
+            }
+        });
+        cos.write('a');
+        try {
+            cos.enableBuffering(500);
+            fail("should throw IllegalStateException because of late setup of enableBuffering when bytes are already written.");
+        } catch (IllegalStateException e) {
+            System.out.println("this is ok - exception should be thrown: " + e.getMessage());
+            // ok - should be thrown (bytes are already written).
+        }
+    }
+
+    @Test
+    public void testSetStramProviderIllegalStateException1() throws IOException {
+        CommittingOutputStream cos = new CommittingOutputStream();
+        cos.enableBuffering(1);
+        writeAndCheckIllegalState(cos);
+    }
+
+    @Test
+    public void testSetStramProviderIllegalStateException2() throws IOException {
+        CommittingOutputStream cos = new CommittingOutputStream();
+        writeAndCheckIllegalState(cos);
+    }
+
+    private void writeAndCheckIllegalState(CommittingOutputStream cos) throws IOException {
+        try {
+            cos.write('a');
+            cos.write('a');
+            cos.write('a');
+            cos.write('a');
+            cos.write('a');
+            fail("should throw IllegalStateException because of missing stream provider (bytes are already written).");
+        } catch (IllegalStateException e) {
+            System.out.println("this is ok - exception should be thrown: " + e.getMessage());
+            // ok - should be thrown (bytes are already written).
+        }
+    }
+
+}
+
+
+
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java
new file mode 100644
index 0000000..77ce25b
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/CookiesParserTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, 2018 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.common.message.internal;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import javax.ws.rs.core.NewCookie;
+
+import org.glassfish.jersey.message.internal.CookiesParser;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class CookiesParserTest {
+
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+    @Test
+    public void testCaseInsensitiveNewCookieParams() throws Exception {
+        _testCaseInsensitiveNewCookieParams("expires", "max-age", "path", "domain", "comment", "version", "secure", "httponly");
+        _testCaseInsensitiveNewCookieParams("Expires", "Max-Age", "Path", "Domain", "Comment", "Version", "Secure", "HttpOnly");
+        _testCaseInsensitiveNewCookieParams("exPires", "max-aGe", "patH", "doMAin", "Comment", "vErsion", "secuRe", "httPonly");
+    }
+
+    private void _testCaseInsensitiveNewCookieParams(final String expires, final String maxAge, final String path,
+                                                     final String domain, final String comment, final String version,
+                                                     final String secure, final String httpOnly) throws Exception {
+
+        final String header = "foo=bar;"
+                + expires + "=Tue, 15 Jan 2013 21:47:38 GMT;"
+                + maxAge + "=42;"
+                + path + "=/;"
+                + domain + "=.example.com;"
+                + comment + "=Testing;"
+                + version + "=1;"
+                + secure + ";"
+                + httpOnly;
+
+        final NewCookie cookie = CookiesParser.parseNewCookie(header);
+
+        assertThat(cookie.getName(), equalTo("foo"));
+        assertThat(cookie.getValue(), equalTo("bar"));
+
+        assertThat(cookie.getExpiry(), equalTo(dateFormat.parse("Tue, 15 Jan 2013 21:47:38 GMT")));
+        assertThat(cookie.getMaxAge(), equalTo(42));
+        assertThat(cookie.getPath(), equalTo("/"));
+        assertThat(cookie.getDomain(), equalTo(".example.com"));
+        assertThat(cookie.getComment(), equalTo("Testing"));
+        assertThat(cookie.getVersion(), equalTo(1));
+        assertThat(cookie.isSecure(), is(true));
+        assertThat(cookie.isHttpOnly(), is(true));
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
new file mode 100644
index 0000000..c212e1d
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/HeaderUtilsTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2013, 2018 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.common.message.internal;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.core.AbstractMultivaluedMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.message.internal.HeaderUtils;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * {@link HeaderUtils} unit tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class HeaderUtilsTest {
+
+    public HeaderUtilsTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @Test
+    public void testCreateInbound() throws Exception {
+        final MultivaluedMap<String, String> inbound = HeaderUtils.createInbound();
+        assertNotNull(inbound);
+
+        // Test mutability.
+        inbound.putSingle("key", "value");
+        assertEquals("value", inbound.getFirst("key"));
+    }
+
+    @Test
+    public void testEmpty() throws Exception {
+        final MultivaluedMap<String, String> emptyStrings = HeaderUtils.empty();
+        assertNotNull(emptyStrings);
+
+        final MultivaluedMap<String, Object> emptyObjects = HeaderUtils.empty();
+        assertNotNull(emptyObjects);
+
+        // Test immutability.
+        try {
+            emptyStrings.putSingle("key", "value");
+            fail("UnsupportedOperationException expected.");
+        } catch (UnsupportedOperationException ex) {
+            // passed
+        }
+        try {
+            emptyObjects.putSingle("key", "value");
+            fail("UnsupportedOperationException expected.");
+        } catch (UnsupportedOperationException ex) {
+            // passed
+        }
+    }
+
+    @Test
+    public void testCreateOutbound() throws Exception {
+        final MultivaluedMap<String, Object> outbound = HeaderUtils.createOutbound();
+        assertNotNull(outbound);
+
+        // Test mutability.
+        outbound.putSingle("key", "value");
+        assertEquals("value", outbound.getFirst("key"));
+
+        final Object value = new Object();
+        outbound.putSingle("key", value);
+        assertEquals(value, outbound.getFirst("key"));
+    }
+
+    @Test
+    public void testAsString() throws Exception {
+        assertNull(HeaderUtils.asString(null, null));
+
+        final String value = "value";
+        assertSame(value, HeaderUtils.asString(value, null));
+
+        final URI uri = new URI("test");
+        assertEquals(uri.toASCIIString(), HeaderUtils.asString(uri, null));
+    }
+
+    @Test
+    public void testAsStringList() throws Exception {
+        assertNotNull(HeaderUtils.asStringList(null, null));
+        assertTrue(HeaderUtils.asStringList(null, null).isEmpty());
+
+        final URI uri = new URI("test");
+        final List<Object> values = new LinkedList<Object>() {{
+            add("value");
+            add(null);
+            add(uri);
+        }};
+
+        // test string values
+        final List<String> stringList = HeaderUtils.asStringList(values, null);
+        assertEquals(Arrays.asList("value", "[null]", uri.toASCIIString()),
+                     stringList);
+
+        // tests live view
+        values.add("value2");
+        assertEquals(Arrays.asList("value", "[null]", uri.toASCIIString(), "value2"),
+                     stringList);
+        values.remove(1);
+        assertEquals(Arrays.asList("value", uri.toASCIIString(), "value2"),
+                     stringList);
+    }
+
+    @Test
+    public void testAsStringHeaders() throws Exception {
+        assertNull(HeaderUtils.asStringHeaders(null));
+
+        final AbstractMultivaluedMap<String, Object> headers = HeaderUtils.createOutbound();
+
+        headers.putSingle("k1", "value");
+        headers.add("k1", "value2");
+
+        final URI uri = new URI("test");
+        headers.putSingle("k2", uri);
+
+        headers.putSingle("k3", "value3");
+
+        final MultivaluedMap<String, String> stringHeaders = HeaderUtils.asStringHeaders(headers);
+
+        // test string values
+        assertEquals(Arrays.asList("value", "value2"),
+                     stringHeaders.get("k1"));
+        assertEquals(Collections.singletonList(uri.toASCIIString()),
+                     stringHeaders.get("k2"));
+        assertEquals(Collections.singletonList("value3"),
+                     stringHeaders.get("k3"));
+
+        // test live view
+        headers.get("k1").remove(1);
+        headers.add("k2", "value4");
+        headers.remove("k3");
+
+        assertEquals(Collections.singletonList("value"),
+                     stringHeaders.get("k1"));
+        assertEquals(Arrays.asList(uri.toASCIIString(), "value4"),
+                     stringHeaders.get("k2"));
+        assertFalse(stringHeaders.containsKey("k3"));
+    }
+
+    @Test
+    public void testAsHeaderString() throws Exception {
+        assertNull(HeaderUtils.asHeaderString(null, null));
+
+        final URI uri = new URI("test");
+        final List<Object> values = Arrays.asList("value", null, uri);
+
+        // test string values
+        final String result = HeaderUtils.asHeaderString(values, null);
+        assertEquals("value,[null]," + uri.toASCIIString(), result);
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/InboundMessageContextTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/InboundMessageContextTest.java
new file mode 100644
index 0000000..3645513
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/InboundMessageContextTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.message.internal;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.message.internal.CookieProvider;
+import org.glassfish.jersey.message.internal.InboundMessageContext;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * {@link org.glassfish.jersey.message.internal.InboundMessageContext} test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class InboundMessageContextTest {
+
+    public InboundMessageContextTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    private static InboundMessageContext createInboundMessageContext() {
+        return new InboundMessageContext() {
+            @Override
+            protected Iterable<ReaderInterceptor> getReaderInterceptors() {
+                return Collections.emptyList();
+            }
+        };
+    }
+
+    @Test
+    public void testNoLength() {
+        InboundMessageContext r = createInboundMessageContext();
+        assertEquals(-1, r.getLength());
+    }
+
+    @Test
+    public void testRequestCookies() throws URISyntaxException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.COOKIE, "oreo=chocolate");
+        r.header(HttpHeaders.COOKIE, "nilla=vanilla");
+        assertEquals(r.getRequestCookies().size(), 2);
+        assertTrue(r.getRequestCookies().containsKey("oreo"));
+        assertTrue(r.getRequestCookies().containsKey("nilla"));
+        CookieProvider cp = new CookieProvider();
+        assertTrue(r.getRequestCookies().containsValue(cp.fromString("oreo=chocolate")));
+        assertTrue(r.getRequestCookies().containsValue(cp.fromString("nilla=vanilla")));
+    }
+
+    @Test
+    public void testDate() throws URISyntaxException, ParseException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.DATE, "Tue, 29 Jan 2002 22:14:02 -0500");
+        SimpleDateFormat f = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
+        Date date = f.parse("Tue, 29 Jan 2002 22:14:02 -0500");
+        assertEquals(r.getDate(), date);
+    }
+
+    @Test
+    public void testHeader() throws URISyntaxException, ParseException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.ACCEPT, "application/xml, text/plain");
+        r.header(HttpHeaders.ACCEPT, "application/json");
+        r.header("FOO", "");
+        assertTrue(r.getHeaderString(HttpHeaders.ACCEPT).contains("application/xml"));
+        assertTrue(r.getHeaderString(HttpHeaders.ACCEPT).contains("text/plain"));
+        assertTrue(r.getHeaderString(HttpHeaders.ACCEPT).contains("application/json"));
+        assertEquals(r.getHeaderString("FOO").length(), 0);
+        assertNull(r.getHeaderString("BAR"));
+    }
+
+    @Test
+    public void testHeaderMap() throws URISyntaxException, ParseException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.ACCEPT, "application/xml, text/plain");
+        r.header(HttpHeaders.ACCEPT, "application/json");
+        r.header("Allow", "GET, PUT");
+        r.header("Allow", "POST");
+        assertTrue(r.getHeaders().containsKey(HttpHeaders.ACCEPT));
+        assertTrue(r.getHeaders().containsKey("Allow"));
+        assertTrue(r.getHeaders().get(HttpHeaders.ACCEPT).contains("application/json"));
+        assertTrue(r.getHeaders().get("Allow").contains("POST"));
+    }
+
+    @Test
+    public void testAllowedMethods() throws URISyntaxException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header("Allow", "GET, PUT");
+        r.header("Allow", "POST");
+        assertEquals(3, r.getAllowedMethods().size());
+        assertTrue(r.getAllowedMethods().contains("GET"));
+        assertTrue(r.getAllowedMethods().contains("PUT"));
+        assertTrue(r.getAllowedMethods().contains("POST"));
+        assertFalse(r.getAllowedMethods().contains("DELETE"));
+    }
+
+    @Test
+    public void testResponseCookies() throws URISyntaxException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.SET_COOKIE, "oreo=chocolate");
+        r.header(HttpHeaders.SET_COOKIE, "nilla=vanilla");
+        assertEquals(2, r.getResponseCookies().size());
+        assertTrue(r.getResponseCookies().containsKey("oreo"));
+        assertTrue(r.getResponseCookies().containsKey("nilla"));
+    }
+
+    @Test
+    public void testEntityTag() throws URISyntaxException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.ETAG, "\"tag\"");
+        assertEquals(EntityTag.valueOf("\"tag\""), r.getEntityTag());
+    }
+
+    @Test
+    public void testLastModified() throws URISyntaxException, ParseException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.LAST_MODIFIED, "Tue, 29 Jan 2002 22:14:02 -0500");
+        SimpleDateFormat f = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
+        Date date = f.parse("Tue, 29 Jan 2002 22:14:02 -0500");
+        assertEquals(date, r.getLastModified());
+    }
+
+    @Test
+    public void testLocation() throws URISyntaxException {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.LOCATION, "http://example.org/app");
+        assertEquals(URI.create("http://example.org/app"), r.getLocation());
+    }
+
+    @Test
+    public void testGetLinks() {
+        InboundMessageContext r = createInboundMessageContext();
+        Link link1 = Link.fromUri("http://example.org/app/link1").param("produces", "application/json").param("method",
+                "GET").rel("self").build();
+        Link link2 = Link.fromUri("http://example.org/app/link2").param("produces", "application/xml").param("method",
+                "PUT").rel("self").build();
+        r.header("Link", link1.toString());
+        r.header("Link", link2.toString());
+        assertEquals(2, r.getLinks().size());
+        assertTrue(r.getLinks().contains(link1));
+        assertTrue(r.getLinks().contains(link2));
+    }
+
+    @Test
+    public void testGetLinksWithCommaInUri() {
+        InboundMessageContext r = createInboundMessageContext();
+        Link link1 = Link.fromUri("http://example.org/app/foo,bar").param("produces", "application/json").param("method",
+                "GET").rel("self").build();
+        r.header("Link", link1.toString());
+        assertEquals(1, r.getLinks().size());
+        assertTrue(r.getLinks().contains(link1));
+    }
+
+    @Test
+    public void testGetLinksWithMultipleLinksInOneHeaderAndCommaInUri() {
+        InboundMessageContext r = createInboundMessageContext();
+        Link link1 = Link.fromUri("https://example.org/one/api/groups?foo,page=2").rel("next").build();
+        Link link2 = Link.fromUri("https://example.org/one/api/groups?bar,page=39").rel("last").build();
+        r.header("Link", "<https://example.org/one/api/groups?foo,page=2>; rel=\"next\", <https://example.org/one/api/groups?bar,page=39>; rel=\"last\"");
+        assertEquals(2, r.getLinks().size());
+        assertTrue(r.getLinks().contains(link1));
+        assertTrue(r.getLinks().contains(link2));
+    }
+
+    @Test
+    public void testGetLinksWithMultipleLinksInOneHeader() {
+        InboundMessageContext r = createInboundMessageContext();
+        Link link1 = Link.fromUri("https://example.org/one/api/groups?page=2").rel("next").build();
+        Link link2 = Link.fromUri("https://example.org/one/api/groups?page=39").rel("last").build();
+        r.header("Link", "<https://example.org/one/api/groups?page=2>; rel=\"next\", <https://example.org/one/api/groups?page=39>; rel=\"last\"");
+        assertEquals(2, r.getLinks().size());
+        assertTrue(r.getLinks().contains(link1));
+        assertTrue(r.getLinks().contains(link2));
+    }
+
+    @Test
+    public void testGetLinksWithMultipleLinksInOneHeaderWithLtInValue() {
+        InboundMessageContext r = createInboundMessageContext();
+        Link link1 = Link.fromUri("https://example.org/one/api/groups?page=2").rel("next").param("foo", "<bar>").build();
+        Link link2 = Link.fromUri("https://example.org/one/api/groups?page=39").rel("last").param("bar", "<<foo").build();
+        r.header("Link", "<https://example.org/one/api/groups?page=2>; rel=\"next\"; foo=\"<bar>\", <https://example.org/one/api/groups?page=39>; rel=\"last\"; bar=\"<<foo\"");
+        assertEquals(2, r.getLinks().size());
+        assertTrue(r.getLinks().contains(link1));
+        assertTrue(r.getLinks().contains(link2));
+    }
+
+    @Test
+    public void testGetLink() {
+        InboundMessageContext r = createInboundMessageContext();
+        Link link1 = Link.fromUri("http://example.org/app/link1").param("produces", "application/json").param("method",
+                "GET").rel("self").build();
+        Link link2 = Link.fromUri("http://example.org/app/link2").param("produces", "application/xml").param("method",
+                "PUT").rel("update").build();
+        Link link3 = Link.fromUri("http://example.org/app/link2").param("produces", "application/xml").param("method",
+                "POST").rel("update").build();
+        r.header("Link", link1.toString());
+        r.header("Link", link2.toString());
+        r.header("Link", link3.toString());
+        assertTrue(r.getLink("self").equals(link1));
+        assertTrue(r.getLink("update").equals(link2) || r.getLink("update").equals(link3));
+    }
+
+    @Test
+    public void testGetAllowedMethods() {
+        InboundMessageContext r = createInboundMessageContext();
+        r.header(HttpHeaders.ALLOW, "a,B,CcC,dDd");
+        final Set<String> allowedMethods = r.getAllowedMethods();
+        assertEquals(4, allowedMethods.size());
+        assertTrue(allowedMethods.contains("A"));
+        assertTrue(allowedMethods.contains("B"));
+        assertTrue(allowedMethods.contains("CCC"));
+        assertTrue(allowedMethods.contains("DDD"));
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/JerseyLinkTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/JerseyLinkTest.java
new file mode 100644
index 0000000..7e25df8
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/JerseyLinkTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.message.internal;
+
+import java.net.URI;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests for LinkTest class.
+ *
+ * @author Santiago Pericas-Geertsen (Santiago.PericasGeertsen at oracle.com)
+ */
+public class JerseyLinkTest {
+
+    @Path("/myresource")
+    static class MyResource {
+
+        @GET
+        @Produces("text/plain")
+        public String self() {
+            return "myself";
+        }
+
+        @GET
+        @Produces("application/xml")
+        public String notSelf() {
+            return "<xml>notSelf</xml>";
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        RuntimeDelegate.setInstance(null);
+    }
+
+    @Test
+    public void testGetUri() {
+        URI u = URI.create("http://example.org/app/link1");
+        Link l1 = Link.fromUri("http://example.org/app/link1").param("foo1", "bar1").param("foo2", "bar2").build();
+        assertEquals(l1.getUri(), u);
+        assertEquals(l1.getUriBuilder().build(), u);
+    }
+
+    @Test
+    public void testToString() {
+        Link link = Link.fromUri("http://example.org/app/link1").rel("self").build();
+        assertEquals("<http://example.org/app/link1>; rel=\"self\"", link.toString());
+    }
+
+    @Test
+    public void testGetters() {
+        Link link = Link.fromUri("http://example.org/app/link1").rel("self").type("text/plain").build();
+        assertEquals(URI.create("http://example.org/app/link1"), link.getUri());
+        assertEquals("self", link.getRel());
+        assertEquals(null, link.getTitle());
+        assertEquals("text/plain", link.getType());
+        assertEquals(2, link.getParams().size());
+    }
+
+    /**
+     * Regression test for JERSEY-1378 fix.
+     */
+    @Test
+    public void buildRelativeLinkTest() {
+        assertEquals(URI.create("aa%20bb"), Link.fromUri("aa bb").build().getUri());
+    }
+
+    /**
+     * Reproducer for JERSEY-2387: IAE expected on unresolved URI template parameters.
+     */
+    @Test
+    public void testLinkBuilderWithUnresolvedTemplates() {
+        Link.Builder linkBuilder;
+        try {
+            linkBuilder = Link.fromUri("scheme://authority/{x1}/{x2}/{x3}");
+            linkBuilder.build("p");
+            fail("IllegalArgumentException is expected to be thrown from Link.Builder when there are unresolved templates.");
+        } catch (IllegalArgumentException expected) {
+            // exception expected, move on...
+        }
+        try {
+            linkBuilder = Link.fromUri("scheme://authority/{x1}/{x2}/{x3}");
+            linkBuilder.build();
+            fail("IllegalArgumentException is expected to be thrown from Link.Builder when there are unresolved templates.");
+        } catch (IllegalArgumentException expected) {
+            // exception expected, move on...
+        }
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/LinkProviderTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/LinkProviderTest.java
new file mode 100644
index 0000000..0f8f3c7
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/LinkProviderTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.message.internal;
+
+import javax.ws.rs.core.Link;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for LinkProvider class.
+ *
+ * @author Santiago Pericas-Geertsen (Santiago.PericasGeertsen at oracle.com)
+ */
+public class LinkProviderTest {
+
+    @Before
+    public void setUp() throws Exception {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        RuntimeDelegate.setInstance(null);
+    }
+
+    @Test
+    public void testValueOf() {
+        final Link l1 = Link.fromUri("http://example.org/app/link1").build();
+        Link l2 = Link.valueOf("<http://example.org/app/link1>");
+        assertEquals(l1, l2);
+        l2 = Link.valueOf(" <http://example.org/app/link1>");
+        assertEquals(l1, l2);
+        l2 = Link.valueOf(" <http://example.org/app/link1> ");
+        assertEquals(l1, l2);
+    }
+
+    @Test
+    public void testValueOfExceptions() {
+        int nOfExceptions = 0;
+        try {
+            Link.valueOf("http://example.org/app/link1>");
+        } catch (final IllegalArgumentException e) {
+            nOfExceptions++;
+        }
+        try {
+            Link.valueOf("<http://example.org/app/link1");
+        } catch (final IllegalArgumentException e) {
+            nOfExceptions++;
+        }
+        try {
+            Link.valueOf("http://example.org/app/link1");
+        } catch (final IllegalArgumentException e) {
+            nOfExceptions++;
+        }
+        assertEquals(nOfExceptions, 3);
+    }
+
+    @Test
+    public void testValueOfParams() {
+        final Link l1 = Link.fromUri("http://example.org/app/link1").param("foo1", "bar1").param("foo2", "bar2").build();
+        Link l2 = Link.valueOf("<http://example.org/app/link1>; foo1=\"bar1\"; foo2 = \"bar2\"");
+        assertEquals(l1, l2);
+        l2 = Link.valueOf("<http://example.org/app/link1>; foo2=\"bar2\"; foo1= \"bar1\"");
+        assertEquals(l1, l2);
+        l2 = Link.valueOf("<http://example.org/app/link1>;      foo1=\"bar1\";     foo2=\"bar2\"");
+        assertEquals(l1, l2);
+        l2 = Link.valueOf("< http://example.org/app/link1   >;      foo1=\"bar1\";     foo2=\"bar2\"");
+        assertEquals(l1, l2);
+    }
+
+    @Test
+    public void testRoundTrip() {
+        final Link l1 = Link.fromUri("http://example.org/app/link1").param("foo1", "bar1").param("foo2", "bar2").build();
+        assertEquals(l1, Link.valueOf(l1.toString()));
+        final Link l2 = Link.valueOf("<http://example.org/app/link1>; foo1=\"bar1\"; foo2 = \"bar2\"");
+        assertEquals(l1, l2);
+    }
+
+    @Test
+    public void testWithoutDoubleQuotes() {
+        final Link l1 = Link.fromUri("http://example.org/app/link1").param("foo1", "bar1").param("foo2", "bar2").build();
+        assertEquals(l1, Link.valueOf(l1.toString()));
+        final Link l2 = Link.valueOf("<http://example.org/app/link1>; foo1=bar1; foo2 = bar2");
+        assertEquals(l1, l2);
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/MatchingEntityTagTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/MatchingEntityTagTest.java
new file mode 100644
index 0000000..40d457b
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/MatchingEntityTagTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010, 2018 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.common.message.internal;
+
+import java.text.ParseException;
+import java.util.Set;
+
+import javax.ws.rs.core.EntityTag;
+
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+import org.glassfish.jersey.message.internal.MatchingEntityTag;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * {@link MatchingEntityTag} unit tests ported from Jersey 1.x.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class MatchingEntityTagTest {
+
+    @Test
+    public void testOneEntityTag() throws Exception {
+        String header = "\"1\"";
+        Set<MatchingEntityTag> s = HttpHeaderReader.readMatchingEntityTag(header);
+
+        assertEquals(1, s.size());
+
+        assertTrue(s.contains(new EntityTag("1")));
+    }
+
+    @Test
+    public void testMultipleEntityTag() throws Exception {
+        String header = "\"1\", W/\"2\", \"3\"";
+        Set<MatchingEntityTag> s = HttpHeaderReader.readMatchingEntityTag(header);
+
+        assertEquals(3, s.size());
+
+        assertTrue(s.contains(new EntityTag("1")));
+
+        assertTrue(s.contains(new EntityTag("2", true)));
+
+        assertTrue(s.contains(new EntityTag("3")));
+    }
+
+    @Test
+    public void testAnyMatch() throws Exception {
+        String header = "*";
+        Set<MatchingEntityTag> s = HttpHeaderReader.readMatchingEntityTag(header);
+
+        assertThat(s.size(), is(equalTo(0)));
+        assertThat(MatchingEntityTag.ANY_MATCH, is(s));
+    }
+
+    /**
+     * Reproducer for JERSEY-1278.
+     */
+    @Test
+    public void testBadEntityTag() {
+        String header = "1\"";
+        try {
+            HttpHeaderReader.readMatchingEntityTag(header);
+            fail("ParseException expected");
+        } catch (ParseException pe) {
+            assertThat(pe.getMessage(), containsString(header));
+        }
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/MediaTypesTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/MediaTypesTest.java
new file mode 100644
index 0000000..12dcd81
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/MediaTypesTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2013, 2018 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.common.message.internal;
+
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+import org.glassfish.jersey.message.internal.MediaTypes;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * MediaTypes utility method tests.
+ *
+ * @author Miroslav Fuksa
+ * @author Marek Potociar (marek.potociar at oralce.com)
+ */
+public class MediaTypesTest {
+
+    @Test
+    public void testConvertToString() {
+        final List<MediaType> emptyList = Collections.emptyList();
+        Assert.assertEquals("", MediaTypes.convertToString(emptyList));
+
+
+        Assert.assertEquals("\"text/plain\"", MediaTypes.convertToString(Collections.singleton(MediaType.TEXT_PLAIN_TYPE)));
+
+        Assert.assertEquals("\"text/plain\", \"application/json\"",
+                MediaTypes.convertToString(Arrays.asList(MediaType.TEXT_PLAIN_TYPE,
+                                                         MediaType.APPLICATION_JSON_TYPE)));
+
+        Assert.assertEquals("\"text/plain\", \"application/json\", \"text/html\"",
+                            MediaTypes.convertToString(Arrays.asList(MediaType.TEXT_PLAIN_TYPE,
+                                                                     MediaType.APPLICATION_JSON_TYPE,
+                                                                     MediaType.TEXT_HTML_TYPE)));
+    }
+
+    @Test
+    public void testMostSpecific() {
+
+        MediaType m1;
+        MediaType m2;
+
+        /* wildcard type */
+        m1 = MediaType.WILDCARD_TYPE;
+
+        // wildcard type #1 - concrete type wins
+        m2 = new MediaType("foo", "bar");
+        _testMostSpecific(m1, m2, m2);
+        _testMostSpecific(m2, m1, m2);
+
+        // wildcard type #2 - wildcard subtype wins
+        m2 = new MediaType("foo", "*");
+        _testMostSpecific(m1, m2, m2);
+        _testMostSpecific(m2, m1, m2);
+
+        // wildcard type #3 - first parameter wins
+        m2 = new MediaType("*", "*");
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m2);
+
+        /* wildcard subtype */
+        m1 = new MediaType("moo", "*");
+
+        // wildcard subtype #1 - concrete type wins
+        m2 = new MediaType("foo", "bar");
+        _testMostSpecific(m1, m2, m2);
+        _testMostSpecific(m2, m1, m2);
+
+        // wildcard subtype #2 - first parameter in method wins
+        m2 = new MediaType("foo", "*");
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m2);
+
+        /* concrete types */
+        // concrete types - first parameter in method wins
+        m1 = new MediaType("moo", "boo");
+        m2 = new MediaType("foo", "bar");
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m2);
+
+        /* concrete type with parameters */
+        m1 = new MediaType("foo", "bar", asMap("p1=v1;p2=v2"));
+
+        // concrete type with parameters #1 - wildcard type looses
+        m2 = MediaType.WILDCARD_TYPE;
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m1);
+
+        // concrete type with parameters #2 - wildcard subtype looses
+        m2 = new MediaType("foo", "*");
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m1);
+
+        // concrete type with parameters #3 - concrete parameter-less type looses
+        m2 = new MediaType("foo", "baz");
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m1);
+
+        // concrete type with parameters #4 - type with less parameters type looses
+        m2 = new MediaType("foo", "baz", asMap("a1=b1"));
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m1);
+
+        // both concrete types with parameters #5 - first parameter in method wins
+        m2 = new MediaType("foo", "baz", asMap("a1=b1;a2=b2"));
+        _testMostSpecific(m1, m2, m1);
+        _testMostSpecific(m2, m1, m2);
+    }
+
+    private static void _testMostSpecific(MediaType m1, MediaType m2, MediaType result) {
+        assertThat("Unexpected media type selected to be most specific.",
+                MediaTypes.mostSpecific(m1, m2), is(result));
+    }
+
+    /**
+     * Creates a map from HTTP header parameter strings.
+     *
+     * @param parameters HTTP header parameters string.
+     * @return HTTP header parameters map.
+     */
+    public static Map<String, String> asMap(String parameters) {
+        HttpHeaderReader reader = HttpHeaderReader.newInstance(";" + parameters);
+
+        if (reader.hasNext()) {
+            try {
+                return HttpHeaderReader.readParameters(reader);
+            } catch (ParseException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        return Collections.emptyMap();
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundJaxrsResponseBuilderTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundJaxrsResponseBuilderTest.java
new file mode 100644
index 0000000..fb68693
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundJaxrsResponseBuilderTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.message.internal;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.message.internal.OutboundJaxrsResponse;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * JaxrsResponseViewTest class.
+ *
+ * @author Santiago Pericas-Geertsen (santiago.pericasgeertsen at oracle.com)
+ */
+public class OutboundJaxrsResponseBuilderTest {
+
+    /**
+     * Create test class.
+     */
+    public OutboundJaxrsResponseBuilderTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    /**
+     * Test media type header setting, retrieval.
+     */
+    @Test
+    public void testMediaType() {
+        final Response r = new OutboundJaxrsResponse.Builder(new OutboundMessageContext())
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML)
+                .build();
+        assertEquals(204, r.getStatus());
+        assertEquals(Response.Status.NO_CONTENT, r.getStatusInfo());
+        assertEquals(MediaType.TEXT_HTML_TYPE, r.getMediaType());
+    }
+
+    @Test
+    public void testIssue1297Fix() {
+        final Response response = new OutboundJaxrsResponse.Builder(new OutboundMessageContext())
+                .status(Response.Status.OK)
+                .entity("1234567890")
+                .build();
+        final int len = response.getLength();
+        assertEquals(-1, len);
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundJaxrsResponseTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundJaxrsResponseTest.java
new file mode 100644
index 0000000..0fc241f
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundJaxrsResponseTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2013, 2018 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.common.message.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.message.internal.OutboundJaxrsResponse;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * OutboundJaxrsResponse unit tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class OutboundJaxrsResponseTest {
+
+    private static class TestInputStream extends ByteArrayInputStream {
+
+        private boolean isRead;
+        private boolean isClosed;
+
+        private TestInputStream() {
+            super("test".getBytes());
+        }
+
+        @Override
+        public synchronized int read() {
+            final int read = super.read();
+            isRead = read == -1;
+            return read;
+        }
+
+        @Override
+        public synchronized int read(byte[] b, int off, int len) {
+            final int read = super.read(b, off, len);
+            isRead = read == -1;
+            return read;
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            final int read = super.read(b);
+            isRead = read == -1;
+            return read;
+        }
+
+        @Override
+        public void close() throws IOException {
+            isClosed = true;
+            super.close();
+        }
+    }
+
+    private static final OutboundMessageContext.StreamProvider TEST_PROVIDER
+            = new OutboundMessageContext.StreamProvider() {
+        @Override
+        public OutputStream getOutputStream(int contentLength) throws IOException {
+            return new ByteArrayOutputStream();
+        }
+    };
+
+    private Response.ResponseBuilder rb;
+
+    /**
+     * Create test class.
+     */
+    public OutboundJaxrsResponseTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @Before
+    public void setUp() {
+        rb = new OutboundJaxrsResponse.Builder(new OutboundMessageContext()).status(Response.Status.OK);
+    }
+
+
+    /**
+     * Test of empty entity buffering.
+     */
+    @Test
+    public void testBufferEmptyEntity() {
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.build());
+        r.getContext().setStreamProvider(TEST_PROVIDER);
+
+        assertFalse("Buffer entity should return 'false' if no entity.", r.bufferEntity());
+    }
+
+    /**
+     * Test of non-stream entity buffering.
+     */
+    @Test
+    public void testBufferNonStreamEntity() {
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.entity(new Object()).build());
+        r.getContext().setStreamProvider(TEST_PROVIDER);
+
+        assertFalse("Buffer entity should return 'false' for non-stream entity.", r.bufferEntity());
+    }
+
+    /**
+     * Test of stream entity buffering.
+     */
+    @Test
+    public void testBufferStreamEntity() {
+        TestInputStream tis = new TestInputStream();
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.entity(tis).build());
+        r.getContext().setStreamProvider(TEST_PROVIDER);
+
+        assertTrue("Buffer entity should return 'true' for stream entity.", r.bufferEntity());
+        assertTrue("Second call to buffer entity should return 'true' for stream entity.", r.bufferEntity()); // second call
+        assertTrue("Buffered stream has not been fully read.", tis.isRead);
+        assertTrue("Buffered stream has not been closed after buffering.", tis.isClosed);
+    }
+
+    /**
+     * Test of closing response with empty entity.
+     */
+    @Test
+    public void testCloseEmptyEntity() {
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.build());
+        r.getContext().setStreamProvider(TEST_PROVIDER);
+
+        r.close();
+        try {
+            r.bufferEntity();
+            fail("IllegalStateException expected when buffering entity on closed response.");
+        } catch (IllegalStateException ex) {
+            // ok
+        }
+        r.close(); // second call should pass
+    }
+
+    /**
+     * Test of closing response with non-stream entity.
+     */
+    @Test
+    public void testCloseNonStreamEntity() {
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.entity(new Object()).build());
+        r.getContext().setStreamProvider(TEST_PROVIDER);
+
+        r.close();
+        try {
+            r.bufferEntity();
+            fail("IllegalStateException expected when buffering entity on closed response.");
+        } catch (IllegalStateException ex) {
+            // ok
+        }
+        r.close(); // second call should pass
+    }
+
+    /**
+     * Test of closing response with stream entity.
+     */
+    @Test
+    public void testCloseStreamEntity() {
+        TestInputStream tis = new TestInputStream();
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.entity(tis).build());
+        r.getContext().setStreamProvider(TEST_PROVIDER);
+
+        r.close();
+        try {
+            r.bufferEntity();
+            fail("IllegalStateException expected when buffering entity on closed response.");
+        } catch (IllegalStateException ex) {
+            // ok
+        }
+        r.close(); // second call should pass
+
+        assertFalse("Unbuffered closed response stream entity should not be read.", tis.isRead);
+        assertTrue("Closed response stream entity should have been closed.", tis.isClosed);
+    }
+
+    /**
+     * Test of closing response with empty entity.
+     */
+    @Test
+    public void testCloseEmptyEntityNoStreamProvider() {
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.build());
+        r.close();
+        try {
+            r.bufferEntity();
+            fail("IllegalStateException expected when buffering entity on closed response.");
+        } catch (IllegalStateException ex) {
+            // ok
+        }
+        r.close(); // second call should pass
+    }
+
+    /**
+     * Test of closing response with non-stream entity.
+     */
+    @Test
+    public void testCloseNonStreamEntityNoStreamProvider() {
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.entity(new Object()).build());
+        r.close();
+        try {
+            r.bufferEntity();
+            fail("IllegalStateException expected when buffering entity on closed response.");
+        } catch (IllegalStateException ex) {
+            // ok
+        }
+        r.close(); // second call should pass
+    }
+
+    /**
+     * Test of closing response with stream entity.
+     */
+    @Test
+    public void testCloseStreamEntityNoStreamProvider() {
+        TestInputStream tis = new TestInputStream();
+        final OutboundJaxrsResponse r = OutboundJaxrsResponse.from(rb.entity(tis).build());
+        r.close();
+        try {
+            r.bufferEntity();
+            fail("IllegalStateException expected when buffering entity on closed response.");
+        } catch (IllegalStateException ex) {
+            // ok
+        }
+        r.close(); // second call should pass
+
+        assertFalse("Unbuffered closed response stream entity should not be read.", tis.isRead);
+        assertTrue("Closed response stream entity should have been closed.", tis.isClosed);
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java
new file mode 100644
index 0000000..cdf2a03
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/OutboundMessageContextTest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.message.internal;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.message.internal.CookieProvider;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+import org.glassfish.jersey.tests.e2e.common.TestRuntimeDelegate;
+
+import org.junit.Test;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * {@link OutboundMessageContext} test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class OutboundMessageContextTest {
+
+    public OutboundMessageContextTest() {
+        RuntimeDelegate.setInstance(new TestRuntimeDelegate());
+    }
+
+    @Test
+    public void testAcceptableMediaTypes() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+
+        r.getHeaders().add(HttpHeaders.ACCEPT, "application/xml, text/plain");
+        r.getHeaders().add(HttpHeaders.ACCEPT, "application/json");
+
+        final List<MediaType> acceptableMediaTypes = r.getAcceptableMediaTypes();
+
+        assertThat(acceptableMediaTypes.size(), equalTo(3));
+        assertThat(acceptableMediaTypes,
+                contains(MediaType.APPLICATION_XML_TYPE, MediaType.TEXT_PLAIN_TYPE, MediaType.APPLICATION_JSON_TYPE));
+    }
+
+    @Test
+    public void testAcceptableLanguages() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.ACCEPT_LANGUAGE, "en-gb;q=0.8, en;q=0.7");
+        r.getHeaders().add(HttpHeaders.ACCEPT_LANGUAGE, "de");
+        assertEquals(r.getAcceptableLanguages().size(), 3);
+        assertTrue(r.getAcceptableLanguages().contains(Locale.UK));
+        assertTrue(r.getAcceptableLanguages().contains(Locale.ENGLISH));
+        assertTrue(r.getAcceptableLanguages().contains(Locale.GERMAN));
+    }
+
+    @Test
+    public void testRequestCookies() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.COOKIE, "oreo=chocolate");
+        r.getHeaders().add(HttpHeaders.COOKIE, "nilla=vanilla");
+        assertEquals(r.getRequestCookies().size(), 2);
+        assertTrue(r.getRequestCookies().containsKey("oreo"));
+        assertTrue(r.getRequestCookies().containsKey("nilla"));
+        CookieProvider cp = new CookieProvider();
+        assertTrue(r.getRequestCookies().containsValue(cp.fromString("oreo=chocolate")));
+        assertTrue(r.getRequestCookies().containsValue(cp.fromString("nilla=vanilla")));
+    }
+
+    @Test
+    public void testDate() throws URISyntaxException, ParseException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.DATE, "Tue, 29 Jan 2002 22:14:02 -0500");
+        SimpleDateFormat f = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
+        Date date = f.parse("Tue, 29 Jan 2002 22:14:02 -0500");
+        assertEquals(r.getDate(), date);
+    }
+
+    @Test
+    public void testHeader() throws URISyntaxException, ParseException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.ACCEPT, "application/xml, text/plain");
+        r.getHeaders().add(HttpHeaders.ACCEPT, "application/json");
+        r.getHeaders().add("FOO", "");
+        assertTrue(r.getHeaderString(HttpHeaders.ACCEPT).contains("application/xml"));
+        assertTrue(r.getHeaderString(HttpHeaders.ACCEPT).contains("text/plain"));
+        assertTrue(r.getHeaderString(HttpHeaders.ACCEPT).contains("application/json"));
+        assertEquals(r.getHeaderString("FOO").length(), 0);
+        assertNull(r.getHeaderString("BAR"));
+    }
+
+    @Test
+    public void testHeaderMap() throws URISyntaxException, ParseException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.ACCEPT, "application/xml, text/plain");
+        r.getHeaders().add(HttpHeaders.ACCEPT, "application/json");
+        r.getHeaders().add("Allow", "GET, PUT");
+        r.getHeaders().add("Allow", "POST");
+        assertTrue(r.getHeaders().containsKey(HttpHeaders.ACCEPT));
+        assertTrue(r.getHeaders().containsKey("Allow"));
+        assertTrue(r.getHeaders().get(HttpHeaders.ACCEPT).contains("application/json"));
+        assertTrue(r.getHeaders().get("Allow").contains("POST"));
+    }
+
+    @Test
+    public void testAllowedMethods() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add("Allow", "GET, PUT");
+        r.getHeaders().add("Allow", "POST");
+        assertEquals(3, r.getAllowedMethods().size());
+        assertTrue(r.getAllowedMethods().contains("GET"));
+        assertTrue(r.getAllowedMethods().contains("PUT"));
+        assertTrue(r.getAllowedMethods().contains("POST"));
+        assertFalse(r.getAllowedMethods().contains("DELETE"));
+    }
+
+    @Test
+    public void testResponseCookies() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.SET_COOKIE, "oreo=chocolate");
+        r.getHeaders().add(HttpHeaders.SET_COOKIE, "nilla=vanilla");
+        assertEquals(2, r.getResponseCookies().size());
+        assertTrue(r.getResponseCookies().containsKey("oreo"));
+        assertTrue(r.getResponseCookies().containsKey("nilla"));
+    }
+
+    @Test
+    public void testEntityTag() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.ETAG, "\"tag\"");
+        assertEquals(EntityTag.valueOf("\"tag\""), r.getEntityTag());
+    }
+
+    @Test
+    public void testLastModified() throws URISyntaxException, ParseException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.LAST_MODIFIED, "Tue, 29 Jan 2002 22:14:02 -0500");
+        SimpleDateFormat f = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
+        Date date = f.parse("Tue, 29 Jan 2002 22:14:02 -0500");
+        assertEquals(date, r.getLastModified());
+    }
+
+    @Test
+    public void testLocation() throws URISyntaxException {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add(HttpHeaders.LOCATION, "http://example.org/app");
+        assertEquals(URI.create("http://example.org/app"), r.getLocation());
+    }
+
+    @Test
+    public void testGetLinks() {
+        OutboundMessageContext r = new OutboundMessageContext();
+        Link link1 = Link.fromUri("http://example.org/app/link1").param("produces", "application/json").param("method", "GET").rel("self").build();
+        Link link2 = Link.fromUri("http://example.org/app/link2").param("produces", "application/xml").param("method", "PUT").rel("self").build();
+        r.getHeaders().add("Link", link1.toString());
+        r.getHeaders().add("Link", link2.toString());
+        assertEquals(2, r.getLinks().size());
+        assertTrue(r.getLinks().contains(link1));
+        assertTrue(r.getLinks().contains(link2));
+    }
+
+    @Test
+    public void testGetLink() {
+        OutboundMessageContext r = new OutboundMessageContext();
+        Link link1 = Link.fromUri("http://example.org/app/link1").param("produces", "application/json").param("method", "GET").rel("self").build();
+        Link link2 = Link.fromUri("http://example.org/app/link2").param("produces", "application/xml").param("method", "PUT").rel("update").build();
+        Link link3 = Link.fromUri("http://example.org/app/link2").param("produces", "application/xml").param("method", "POST").rel("update").build();
+        r.getHeaders().add("Link", link1.toString());
+        r.getHeaders().add("Link", link2.toString());
+        r.getHeaders().add("Link", link3.toString());
+        assertTrue(r.getLink("self").equals(link1));
+        assertTrue(r.getLink("update").equals(link2) || r.getLink("update").equals(link3));
+    }
+
+    @Test
+    public void testGetLength() {
+        OutboundMessageContext r = new OutboundMessageContext();
+        r.getHeaders().add("Content-Length", 50);
+        assertEquals(50, r.getLengthLong());
+    }
+
+    @Test
+    public void testGetLength_tooLongForInt() {
+        OutboundMessageContext r = new OutboundMessageContext();
+        long length = Integer.MAX_VALUE + 5L;
+        r.getHeaders().add("Content-Length", length);
+
+
+        assertEquals(length, r.getLengthLong());
+
+        // value is not a valid integer -> ProcessingException is thrown.
+        try {
+            r.getLength();
+        } catch (ProcessingException e) {
+            return;
+        }
+
+        fail();
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/QualitySourceMediaTypeTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/QualitySourceMediaTypeTest.java
new file mode 100644
index 0000000..249bc6c
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/message/internal/QualitySourceMediaTypeTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.message.internal;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.QualitySourceMediaType;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Quality source media type unit tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class QualitySourceMediaTypeTest {
+    @Parameterized.Parameters
+    // expected result, media type, quality source media type
+    public static List<Object[]> testBeds() {
+        return Arrays.asList(new Object[][]{
+                {Boolean.TRUE, MediaType.APPLICATION_JSON_TYPE, new QualitySourceMediaType("application", "json")},
+                {Boolean.TRUE, MediaType.APPLICATION_JSON_TYPE, new QualitySourceMediaType("application", "json", 1000, null)},
+                {Boolean.FALSE, MediaType.APPLICATION_JSON_TYPE, new QualitySourceMediaType("application", "json", 500, null)},
+                {Boolean.FALSE, MediaType.APPLICATION_JSON_TYPE, new QualitySourceMediaType("application", "xml")}
+        });
+    }
+
+    private final boolean expectEquality;
+    private final MediaType mediaType;
+    private final QualitySourceMediaType qsMediaType;
+
+    public QualitySourceMediaTypeTest(boolean expectEquality, MediaType mediaType, QualitySourceMediaType qsMediaType) {
+        this.expectEquality = expectEquality;
+        this.mediaType = mediaType;
+        this.qsMediaType = qsMediaType;
+    }
+
+    @Test
+    public void testEquals() throws Exception {
+        if (expectEquality) {
+            Assert.assertEquals("Types not equal.", mediaType, qsMediaType);
+            Assert.assertEquals("Types not equal.", qsMediaType, mediaType);
+            Assert.assertEquals(
+                    String.format("Hash codes not equal for %s and %s.", mediaType.toString(), qsMediaType.toString()),
+                    mediaType.hashCode(), qsMediaType.hashCode());
+        } else {
+            Assert.assertFalse(String.format("False equality of %s and %s", mediaType.toString(), qsMediaType.toString()),
+                    qsMediaType.equals(mediaType));
+        }
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/model/internal/CommonConfigTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/model/internal/CommonConfigTest.java
new file mode 100644
index 0000000..c96f28b
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/model/internal/CommonConfigTest.java
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.model.internal;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.Priorities;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.ProviderBinder;
+import org.glassfish.jersey.internal.inject.Providers;
+import org.glassfish.jersey.model.ContractProvider;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
+import org.glassfish.jersey.model.internal.RankedComparator;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test cases for {@link javax.ws.rs.core.Configuration}.
+ *
+ * @author Michal Gajdos
+ */
+public class CommonConfigTest {
+
+    private CommonConfig config;
+
+    @Before
+    public void setUp() throws Exception {
+        config = new CommonConfig(null, ComponentBag.INCLUDE_ALL);
+    }
+
+    @Test
+    public void testGetProperties() throws Exception {
+        try {
+            config.getConfiguration().getProperties().put("foo", "bar");
+            fail("Returned properties collection should be immutable.");
+        } catch (final Exception e) {
+            // OK.
+        }
+    }
+
+    @Test
+    public void testSetProperties() throws Exception {
+        config = config.property("foo", "bar");
+        assertEquals("bar", config.getConfiguration().getProperty("foo"));
+
+        final Map<String, String> properties = new HashMap<>();
+        properties.put("hello", "world");
+        config = config.setProperties(properties);
+
+        assertEquals(1, config.getConfiguration().getProperties().size());
+        assertEquals("world", config.getConfiguration().getProperty("hello"));
+
+        properties.put("one", "two");
+        assertEquals(1, config.getConfiguration().getProperties().size());
+        assertNull(config.getConfiguration().getProperty("one"));
+
+        config = config.setProperties(new HashMap<>());
+        assertTrue(config.getConfiguration().getProperties().isEmpty());
+    }
+
+    @Test
+    public void testSetGetProperty() throws Exception {
+        config = config.property("foo", "bar");
+        assertEquals("bar", config.getConfiguration().getProperty("foo"));
+
+        config.property("hello", "world");
+        config.property("foo", null);
+
+        assertEquals(null, config.getConfiguration().getProperty("foo"));
+        assertEquals(1, config.getConfiguration().getProperties().size());
+    }
+
+    public static class EmptyFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext configuration) {
+            return true;
+        }
+    }
+
+    public static class UnconfigurableFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext configuration) {
+            return false;
+        }
+    }
+
+    public static class ComplexEmptyProvider implements ReaderInterceptor, ContainerRequestFilter, ExceptionMapper {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+            // Do nothing.
+        }
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            return context.proceed();
+        }
+
+        @Override
+        public Response toResponse(final Throwable exception) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public static class ComplexEmptyProviderFeature extends ComplexEmptyProvider implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext configuration) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    @Test
+    public void testReplaceWith() throws Exception {
+        config.property("foo", "bar");
+        final EmptyFeature emptyFeature = new EmptyFeature();
+        config.register(emptyFeature);
+        config.register(ComplexEmptyProvider.class, ExceptionMapper.class);
+
+        final CommonConfig other = new CommonConfig(null, ComponentBag.INCLUDE_ALL);
+        other.property("foo", "baz");
+        other.register(UnconfigurableFeature.class);
+        other.register(ComplexEmptyProvider.class, ReaderInterceptor.class, ContainerRequestFilter.class);
+
+        assertEquals("baz", other.getProperty("foo"));
+        assertEquals(1, other.getProperties().size());
+        assertEquals(2, other.getClasses().size());
+        assertEquals(0, other.getInstances().size());
+        assertEquals(2, other.getContracts(ComplexEmptyProvider.class).size());
+        assertTrue(other.getContracts(ComplexEmptyProvider.class).containsKey(ReaderInterceptor.class));
+        assertTrue(other.getContracts(ComplexEmptyProvider.class).containsKey(ContainerRequestFilter.class));
+
+        other.loadFrom(config);
+
+        assertEquals("bar", other.getProperty("foo"));
+        assertEquals(1, other.getProperties().size());
+        assertEquals(1, other.getClasses().size());
+        assertEquals(1, other.getInstances().size());
+        assertEquals(1, other.getContracts(ComplexEmptyProvider.class).size());
+        assertSame(ExceptionMapper.class, other.getContracts(ComplexEmptyProvider.class).keySet().iterator().next());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testGetFeatures() throws Exception {
+        final EmptyFeature emptyFeature = new EmptyFeature();
+        final UnconfigurableFeature unconfigurableFeature = new UnconfigurableFeature();
+        final ComplexEmptyProviderFeature providerFeature = new ComplexEmptyProviderFeature();
+
+        config.register(emptyFeature);
+        config.register(unconfigurableFeature);
+        config.register(providerFeature, ReaderInterceptor.class);
+
+        assertFalse(config.getConfiguration().isEnabled(emptyFeature));
+        assertFalse(config.getConfiguration().isEnabled(unconfigurableFeature));
+        assertFalse(config.getConfiguration().isEnabled(providerFeature));
+
+        assertTrue(config.getConfiguration().isRegistered(emptyFeature));
+        assertTrue(config.getConfiguration().isRegistered(unconfigurableFeature));
+        assertTrue(config.getConfiguration().isRegistered(providerFeature));
+    }
+
+
+    @Test
+    // Regression test for JERSEY-1638.
+    public void testGetNonExistentProviderContractsASEmptyMap() throws Exception {
+        assertTrue(config.getConfiguration().getContracts(CommonConfigTest.class).isEmpty());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testGetClasses() throws Exception {
+        _testCollectionsCommon("GetProviderClasses", config.getClasses(), EmptyFeature.class);
+
+        config.register(ComplexEmptyProviderFeature.class,
+                WriterInterceptor.class, ReaderInterceptor.class, ContainerRequestFilter.class);
+        assertEquals(1, config.getClasses().size());
+
+        config.register(EmptyFeature.class);
+
+        final Set<Class<?>> providerClasses = config.getClasses();
+        assertEquals(2, providerClasses.size());
+        assertTrue(providerClasses.contains(ComplexEmptyProviderFeature.class));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testGetInstances() throws Exception {
+        _testCollectionsCommon("GetProviderInstances", config.getInstances(), new EmptyFeature());
+
+        final ComplexEmptyProviderFeature providerFeature1 = new ComplexEmptyProviderFeature();
+        config.register(providerFeature1, WriterInterceptor.class);
+        assertEquals(1, config.getInstances().size());
+        assertTrue(config.getInstances().contains(providerFeature1));
+
+        final EmptyFeature emptyFeature = new EmptyFeature();
+        config.register(emptyFeature);
+        assertEquals(2, config.getInstances().size());
+        assertTrue(config.getInstances().contains(emptyFeature));
+
+        final ComplexEmptyProviderFeature providerFeature2 = new ComplexEmptyProviderFeature();
+        config.register(providerFeature2, ReaderInterceptor.class, ContainerRequestFilter.class);
+        assertEquals(2, config.getInstances().size());
+        assertFalse(config.getInstances().contains(providerFeature2));
+    }
+
+    @Test
+    public void testRegisterClass() throws Exception {
+        try {
+            final Class clazz = null;
+            //noinspection ConstantConditions
+            config.register(clazz);
+            fail("Cannot register null.");
+        } catch (final IllegalArgumentException e) {
+            // OK.
+        }
+
+        for (int i = 0; i < 2; i++) {
+            config.register(ComplexEmptyProvider.class);
+        }
+
+        final ContractProvider contractProvider =
+                config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+
+        assertEquals(3, contracts.size());
+        assertTrue(contracts.contains(ReaderInterceptor.class));
+        assertTrue(contracts.contains(ContainerRequestFilter.class));
+        assertTrue(contracts.contains(ExceptionMapper.class));
+
+        assertTrue(config.isRegistered(ComplexEmptyProvider.class));
+    }
+
+    @Test
+    public void testRegisterInstance() throws Exception {
+        try {
+            config.register(null);
+            fail("Cannot register null.");
+        } catch (final IllegalArgumentException e) {
+            // OK.
+        }
+
+        final ComplexEmptyProvider[] ceps = new ComplexEmptyProvider[2];
+        for (int i = 0; i < 2; i++) {
+            ceps[i] = new ComplexEmptyProvider();
+            config.register(ceps[i]);
+        }
+
+        final ContractProvider contractProvider =
+                config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+
+        assertEquals(3, contracts.size());
+        assertTrue(contracts.contains(ReaderInterceptor.class));
+        assertTrue(contracts.contains(ContainerRequestFilter.class));
+        assertTrue(contracts.contains(ExceptionMapper.class));
+
+        assertTrue(config.isRegistered(ComplexEmptyProvider.class));
+        assertTrue(config.isRegistered(ceps[0]));
+        assertFalse(config.isRegistered(ceps[1]));
+    }
+
+    @Test
+    public void testRegisterClassInstanceClash() throws Exception {
+        final ComplexEmptyProvider complexEmptyProvider = new ComplexEmptyProvider();
+
+        config.register(ComplexEmptyProvider.class);
+        config.register(complexEmptyProvider);
+        config.register(ComplexEmptyProvider.class);
+
+        assertTrue(config.getClasses().contains(ComplexEmptyProvider.class));
+        assertFalse(config.getInstances().contains(complexEmptyProvider));
+
+        final ContractProvider contractProvider =
+                config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+
+        assertEquals(3, contracts.size());
+        assertTrue(contracts.contains(ReaderInterceptor.class));
+        assertTrue(contracts.contains(ContainerRequestFilter.class));
+        assertTrue(contracts.contains(ExceptionMapper.class));
+    }
+
+    @Test
+    public void testRegisterClassBingingPriority() throws Exception {
+        try {
+            final Class clazz = null;
+            //noinspection ConstantConditions
+            config.register(clazz, Priorities.USER);
+            fail("Cannot register null.");
+        } catch (final IllegalArgumentException e) {
+            // OK.
+        }
+
+        for (final int priority : new int[]{Priorities.USER, Priorities.AUTHENTICATION}) {
+            config.register(ComplexEmptyProvider.class, priority);
+
+            final ContractProvider contractProvider =
+                    config.getComponentBag().getModel(ComplexEmptyProvider.class);
+            final Set<Class<?>> contracts = contractProvider.getContracts();
+
+            assertEquals(3, contracts.size());
+            assertTrue(contracts.contains(ReaderInterceptor.class));
+            assertTrue(contracts.contains(ContainerRequestFilter.class));
+            assertTrue(contracts.contains(ExceptionMapper.class));
+
+            // All priorities are the same.
+            assertEquals(Priorities.USER, contractProvider.getPriority(ReaderInterceptor.class));
+            assertEquals(Priorities.USER, contractProvider.getPriority(ContainerRequestFilter.class));
+            assertEquals(Priorities.USER, contractProvider.getPriority(ExceptionMapper.class));
+        }
+    }
+
+    @Test
+    public void testRegisterInstanceBingingPriority() throws Exception {
+        try {
+            config.register(null, Priorities.USER);
+            fail("Cannot register null.");
+        } catch (final IllegalArgumentException e) {
+            // OK.
+        }
+
+        final Class<ComplexEmptyProvider> providerClass = ComplexEmptyProvider.class;
+
+        for (final int priority : new int[]{Priorities.USER, Priorities.AUTHENTICATION}) {
+            config.register(providerClass, priority);
+
+            final CommonConfig commonConfig = config;
+            final ContractProvider contractProvider =
+                    commonConfig.getComponentBag().getModel(providerClass);
+            final Set<Class<?>> contracts = contractProvider.getContracts();
+
+            assertEquals(3, contracts.size()); // Feature is not there.
+            assertTrue(contracts.contains(ReaderInterceptor.class));
+            assertTrue(contracts.contains(ContainerRequestFilter.class));
+            assertTrue(contracts.contains(ExceptionMapper.class));
+
+            // All priorities are the same.
+            assertEquals(Priorities.USER, contractProvider.getPriority(ReaderInterceptor.class));
+            assertEquals(Priorities.USER, contractProvider.getPriority(ContainerRequestFilter.class));
+            assertEquals(Priorities.USER, contractProvider.getPriority(ExceptionMapper.class));
+        }
+    }
+
+    @Test
+    public void testRegisterClassInstanceBindingPriorityClash() throws Exception {
+        final ComplexEmptyProvider complexEmptyProvider = new ComplexEmptyProvider();
+
+        config.register(ComplexEmptyProvider.class, Priorities.AUTHENTICATION);
+        config.register(complexEmptyProvider, Priorities.USER);
+
+
+        assertTrue(config.getClasses().contains(ComplexEmptyProvider.class));
+        assertFalse(config.getInstances().contains(complexEmptyProvider));
+
+        final ComponentBag componentBag = config.getComponentBag();
+        final ContractProvider contractProvider =
+                componentBag.getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+
+        assertEquals(3, contracts.size()); // Feature is not there.
+        assertTrue(contracts.contains(ReaderInterceptor.class));
+        assertTrue(contracts.contains(ContainerRequestFilter.class));
+        assertTrue(contracts.contains(ExceptionMapper.class));
+
+        // All priorities are the same.
+        assertEquals(Priorities.AUTHENTICATION, contractProvider.getPriority(ReaderInterceptor.class));
+        assertEquals(Priorities.AUTHENTICATION, contractProvider.getPriority(ContainerRequestFilter.class));
+        assertEquals(Priorities.AUTHENTICATION, contractProvider.getPriority(ExceptionMapper.class));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRegisterClassContracts() throws Exception {
+        try {
+            final Class clazz = null;
+            //noinspection ConstantConditions
+            config.register(clazz, ReaderInterceptor.class);
+            fail("Cannot register null.");
+        } catch (final IllegalArgumentException e) {
+            // OK.
+        }
+
+        config.register(ComplexEmptyProvider.class,
+                ReaderInterceptor.class, ContainerRequestFilter.class, WriterInterceptor.class);
+        final ContractProvider contractProvider = config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+        assertEquals(2, contracts.size());
+        assertTrue(ReaderInterceptor.class + " is not registered.", contracts.contains(ReaderInterceptor.class));
+        assertTrue(ContainerRequestFilter.class + " is not registered.", contracts.contains(ContainerRequestFilter.class));
+        assertFalse(WriterInterceptor.class + " should not be registered.", contracts.contains(WriterInterceptor.class));
+
+        assertTrue(config.getInstances().isEmpty());
+        assertTrue(config.getClasses().contains(ComplexEmptyProvider.class));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRegisterInstancesContracts() throws Exception {
+        try {
+            config.register(null, ReaderInterceptor.class);
+            fail("Cannot register null.");
+        } catch (final IllegalArgumentException e) {
+            // OK.
+        }
+
+        final ComplexEmptyProvider complexEmptyProvider = new ComplexEmptyProvider();
+        config.register(complexEmptyProvider,
+                ReaderInterceptor.class, ContainerRequestFilter.class, WriterInterceptor.class);
+        final ContractProvider contractProvider = config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+        assertEquals(2, contracts.size());
+        assertTrue(ReaderInterceptor.class + " is not registered.", contracts.contains(ReaderInterceptor.class));
+        assertTrue(ContainerRequestFilter.class + " is not registered.", contracts.contains(ContainerRequestFilter.class));
+        assertFalse(WriterInterceptor.class + " should not be registered.", contracts.contains(WriterInterceptor.class));
+
+        assertTrue(config.getInstances().contains(complexEmptyProvider));
+        assertTrue(config.getClasses().isEmpty());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRegisterClassContractsFeatureNotInvoked() throws Exception {
+        config.register(ComplexEmptyProviderFeature.class, ReaderInterceptor.class);
+        assertFalse(config.getConfiguration().isEnabled(ComplexEmptyProviderFeature.class));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRegisterInstancesContractsFeatureNotInvoked() throws Exception {
+        final ComplexEmptyProviderFeature feature = new ComplexEmptyProviderFeature();
+        config.register(feature, ReaderInterceptor.class);
+        assertFalse(config.getConfiguration().isEnabled(ComplexEmptyProviderFeature.class));
+        assertFalse(config.getConfiguration().isEnabled(feature));
+    }
+
+    @Test
+    public void testRegisterClassNullContracts() throws Exception {
+        config.register(ComplexEmptyProvider.class, (Class) null);
+
+        final ContractProvider contractProvider =
+                config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+
+        assertEquals(0, contracts.size());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRegisterInstanceNullContracts() throws Exception {
+        config.register(new ComplexEmptyProvider(), (Class) null);
+
+        final ContractProvider contractProvider =
+                config.getComponentBag().getModel(ComplexEmptyProvider.class);
+        final Set<Class<?>> contracts = contractProvider.getContracts();
+
+        assertEquals(0, contracts.size());
+    }
+
+    // Reproducer JERSEY-1637
+    @Test
+    public void testRegisterNullOrEmptyContracts() {
+        final ComplexEmptyProvider provider = new ComplexEmptyProvider();
+
+        config.register(ComplexEmptyProvider.class,  (Class<?>[]) null);
+        assertFalse(config.getConfiguration().isRegistered(ComplexEmptyProvider.class));
+
+        config.register(provider,  (Class<?>[]) null);
+        assertFalse(config.getConfiguration().isRegistered(ComplexEmptyProvider.class));
+        assertFalse(config.getConfiguration().isRegistered(provider));
+
+        config.register(ComplexEmptyProvider.class,  new Class[0]);
+        assertFalse(config.getConfiguration().isRegistered(ComplexEmptyProvider.class));
+
+        config.register(provider,  new Class[0]);
+        assertFalse(config.getConfiguration().isRegistered(ComplexEmptyProvider.class));
+        assertFalse(config.getConfiguration().isRegistered(provider));
+    }
+
+    @Priority(300)
+    public static class LowPriorityProvider implements WriterInterceptor, ReaderInterceptor {
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            // Do nothing.
+        }
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            return context.proceed();
+        }
+    }
+
+    @Priority(200)
+    public static class MidPriorityProvider implements WriterInterceptor, ReaderInterceptor {
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            // Do nothing.
+        }
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            return context.proceed();
+        }
+    }
+
+    @Priority(100)
+    public static class HighPriorityProvider implements WriterInterceptor, ReaderInterceptor {
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            // Do nothing.
+        }
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            return context.proceed();
+        }
+    }
+
+    @Test
+    public void testProviderOrderManual() throws Exception {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+
+        config.register(MidPriorityProvider.class, 500);
+        config.register(LowPriorityProvider.class, 20);
+        config.register(HighPriorityProvider.class, 150);
+
+        ProviderBinder.bindProviders(config.getComponentBag(), injectionManager);
+
+        injectionManager.completeRegistration();
+        final Iterable<WriterInterceptor> allProviders =
+                Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>());
+
+        final Iterator<WriterInterceptor> iterator = allProviders.iterator();
+
+        assertEquals(LowPriorityProvider.class, iterator.next().getClass());
+        assertEquals(HighPriorityProvider.class, iterator.next().getClass());
+        assertEquals(MidPriorityProvider.class, iterator.next().getClass());
+        assertFalse(iterator.hasNext());
+    }
+
+    @Test
+    public void testProviderOrderSemiAutomatic() throws Exception {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+
+        config.register(MidPriorityProvider.class, 50);
+        config.register(LowPriorityProvider.class, 2000);
+        config.register(HighPriorityProvider.class);
+
+        ProviderBinder.bindProviders(config.getComponentBag(), injectionManager);
+        injectionManager.completeRegistration();
+        final Iterable<WriterInterceptor> allProviders =
+                Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>());
+
+        final Iterator<WriterInterceptor> iterator = allProviders.iterator();
+
+        assertEquals(MidPriorityProvider.class, iterator.next().getClass());
+        assertEquals(HighPriorityProvider.class, iterator.next().getClass());
+        assertEquals(LowPriorityProvider.class, iterator.next().getClass());
+        assertFalse(iterator.hasNext());
+    }
+
+    @Test
+    public void testProviderOrderAutomatic() throws Exception {
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        config.register(MidPriorityProvider.class);
+        config.register(LowPriorityProvider.class);
+        config.register(HighPriorityProvider.class);
+
+        ProviderBinder.bindProviders(config.getComponentBag(), injectionManager);
+        injectionManager.completeRegistration();
+
+        final Iterable<WriterInterceptor> allProviders =
+                Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>());
+
+        final Iterator<WriterInterceptor> iterator = allProviders.iterator();
+
+        assertEquals(HighPriorityProvider.class, iterator.next().getClass());
+        assertEquals(MidPriorityProvider.class, iterator.next().getClass());
+        assertEquals(LowPriorityProvider.class, iterator.next().getClass());
+        assertFalse(iterator.hasNext());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testProviderOrderDifForContracts() throws Exception {
+        final Map<Class<?>, Integer> contracts = new IdentityHashMap<>();
+
+        contracts.put(WriterInterceptor.class, ContractProvider.NO_PRIORITY);
+        contracts.put(ReaderInterceptor.class, 2000);
+        config.register(MidPriorityProvider.class, contracts);
+        contracts.clear();
+
+        contracts.put(WriterInterceptor.class, ContractProvider.NO_PRIORITY);
+        contracts.put(ReaderInterceptor.class, 1000);
+        config.register(LowPriorityProvider.class, contracts);
+        contracts.clear();
+
+        contracts.put(WriterInterceptor.class, ContractProvider.NO_PRIORITY);
+        contracts.put(ReaderInterceptor.class, 3000);
+        config.register(HighPriorityProvider.class, contracts);
+        contracts.clear();
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ProviderBinder.bindProviders(config.getComponentBag(), injectionManager);
+
+        injectionManager.completeRegistration();
+        final Iterable<WriterInterceptor> writerInterceptors =
+                Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>());
+
+        final Iterator<WriterInterceptor> writerIterator = writerInterceptors.iterator();
+
+        assertEquals(HighPriorityProvider.class, writerIterator.next().getClass());
+        assertEquals(MidPriorityProvider.class, writerIterator.next().getClass());
+        assertEquals(LowPriorityProvider.class, writerIterator.next().getClass());
+        assertFalse(writerIterator.hasNext());
+
+        final Iterable<ReaderInterceptor> readerInterceptors =
+                Providers.getAllProviders(injectionManager, ReaderInterceptor.class, new RankedComparator<>());
+
+        final Iterator<ReaderInterceptor> readerIterator = readerInterceptors.iterator();
+
+        assertEquals(LowPriorityProvider.class, readerIterator.next().getClass());
+        assertEquals(MidPriorityProvider.class, readerIterator.next().getClass());
+        assertEquals(HighPriorityProvider.class, readerIterator.next().getClass());
+        assertFalse(readerIterator.hasNext());
+    }
+
+
+    private <T> void _testCollectionsCommon(final String testName, final Collection<T> collection, final T element)
+            throws Exception {
+
+        // Not null.
+        assertNotNull(testName + " - returned collection is null.", collection);
+
+        // Immutability.
+        try {
+            collection.add(element);
+            fail(testName + " - returned collection should be immutable.");
+        } catch (final Exception e) {
+            // OK.
+        }
+    }
+
+    public static final class CustomReaderA implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            return null;
+        }
+    }
+
+    public static final class CustomReaderB implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            return null;
+        }
+    }
+
+    public static final class SimpleFeatureA implements Feature {
+
+        private boolean initB;
+
+        public SimpleFeatureA() {
+        }
+
+        public SimpleFeatureA(final boolean initB) {
+            this.initB = initB;
+        }
+
+        @Override
+        public boolean configure(final FeatureContext config) {
+            config.register(initB ? CustomReaderB.class : CustomReaderA.class);
+            return true;
+        }
+    }
+
+    public static final class SimpleFeatureB implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext config) {
+            config.register(CustomReaderB.class);
+            return true;
+        }
+    }
+
+    public static final class InstanceFeatureA implements Feature {
+
+        private boolean initB;
+
+        public InstanceFeatureA() {
+        }
+
+        public InstanceFeatureA(final boolean initB) {
+            this.initB = initB;
+        }
+
+        @Override
+        public boolean configure(final FeatureContext config) {
+            config.register(initB ? new CustomReaderB() : new CustomReaderA());
+            return true;
+        }
+    }
+
+    public static final class ComplexFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext config) {
+            config.register(SimpleFeatureA.class);
+            config.register(SimpleFeatureB.class);
+            return true;
+        }
+    }
+
+    public static final class RecursiveFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext config) {
+            config.register(new CustomReaderA());
+            config.register(RecursiveFeature.class);
+            return true;
+        }
+    }
+
+    public static final class RecursiveInstanceFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext config) {
+            config.register(new CustomReaderA());
+            config.register(new RecursiveInstanceFeature());
+            return true;
+        }
+    }
+
+    @Test
+    public void testConfigureFeatureHierarchy() throws Exception {
+        config.register(ComplexFeature.class);
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+
+        assertTrue(config.getConfiguration().isEnabled(ComplexFeature.class));
+
+        assertTrue(config.getConfiguration().isRegistered(CustomReaderA.class));
+        assertTrue(config.getConfiguration().isRegistered(CustomReaderB.class));
+    }
+
+    @Test
+    public void testConfigureFeatureRecursive() throws Exception {
+        config.register(RecursiveFeature.class);
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+
+        assertTrue(config.getConfiguration().isEnabled(RecursiveFeature.class));
+        assertEquals(1, config.getInstances().size());
+        assertSame(CustomReaderA.class, config.getInstances().iterator().next().getClass());
+    }
+
+    @Test
+    public void testConfigureFeatureInstances() throws Exception {
+        final SimpleFeatureA f1 = new SimpleFeatureA();
+        config.register(f1);
+        final SimpleFeatureA f2 = new SimpleFeatureA(true);
+        config.register(f2);
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+
+        assertTrue(config.getConfiguration().isEnabled(f1));
+        assertFalse(config.getConfiguration().isEnabled(f2));
+
+        assertTrue(config.getConfiguration().isRegistered(CustomReaderA.class));
+        assertFalse(config.getConfiguration().isRegistered(CustomReaderB.class));
+    }
+
+    @Test
+    public void testConfigureFeatureInstancesProviderInstances() throws Exception {
+        final InstanceFeatureA f1 = new InstanceFeatureA();
+        config.register(f1);
+        final InstanceFeatureA f2 = new InstanceFeatureA(true);
+        config.register(f2);
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+
+        assertTrue(config.getConfiguration().isEnabled(f1));
+        assertFalse(config.getConfiguration().isEnabled(f2));
+
+        final Set<Object> providerInstances = config.getInstances();
+        assertEquals(2, providerInstances.size());
+
+        final Set<Object> pureProviderInstances =
+                config.getComponentBag().getInstances(ComponentBag.excludeMetaProviders(injectionManager));
+        assertEquals(1, pureProviderInstances.size());
+
+        int a = 0;
+        int b = 0;
+        for (final Object instance : pureProviderInstances) {
+            if (instance instanceof CustomReaderA) {
+                a++;
+            } else {
+                b++;
+            }
+        }
+        assertEquals(1, a);
+        assertEquals(0, b);
+    }
+
+    @Test
+    public void testConfigureFeatureInstanceRecursive() throws Exception {
+        config.register(new RecursiveInstanceFeature());
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+        assertEquals(0, config.getClasses().size());
+        assertEquals(2, config.getInstances().size());
+        final Set<Object> pureProviders =
+                config.getComponentBag().getInstances(ComponentBag.excludeMetaProviders(injectionManager));
+        assertEquals(1, pureProviders.size());
+        assertSame(CustomReaderA.class, pureProviders.iterator().next().getClass());
+    }
+
+    public static interface Contract {
+    }
+
+    public static class Service implements Contract {
+    }
+
+    public static class ContractBinder extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            bind(Service.class).to(Contract.class);
+        }
+    }
+
+    public static class ContractBinderFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            context.register(new ContractBinder());
+            return true;
+        }
+    }
+
+    @Test
+    public void testBinderConfiguringFeature() throws Exception {
+        config.register(ContractBinderFeature.class);
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+        injectionManager.completeRegistration();
+
+        assertTrue(config.isEnabled(ContractBinderFeature.class));
+        assertEquals(1, config.getInstances().size());
+        assertSame(ContractBinder.class, config.getInstances().iterator().next().getClass());
+
+        final Contract service = injectionManager.getInstance(Contract.class);
+        assertNotNull(service);
+        assertSame(Service.class, service.getClass());
+    }
+
+    public static class InjectMe {
+    }
+
+    public static class InjectIntoFeatureInstance implements Feature {
+
+        @Inject
+        private InjectMe injectMe;
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            context.property("instance-injected", injectMe != null);
+            return true;
+        }
+    }
+
+    public static class InjectIntoFeatureClass implements Feature {
+
+        @Inject
+        private InjectMe injectMe;
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            context.property("class-injected", injectMe != null);
+            return true;
+        }
+    }
+
+    public static class BindInjectMeInFeature implements Feature {
+        @Override
+        public boolean configure(FeatureContext context) {
+            context.register(new AbstractBinder() {
+                @Override
+                protected void configure() {
+                    bind(new InjectMe());
+                }
+            });
+            return true;
+        }
+    }
+
+    @Test
+    public void testFeatureInjections() throws Exception {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+
+        config.register(InjectIntoFeatureClass.class)
+                .register(new InjectIntoFeatureInstance())
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(new InjectMe());
+                    }
+                });
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+
+        assertThat("Feature instance not injected", config.getProperty("instance-injected").toString(), is("true"));
+        assertThat("Feature class not injected", config.getProperty("class-injected").toString(), is("true"));
+    }
+
+    @Test
+    @Ignore
+    public void testFeatureInjectionsBindInFeature() throws Exception {
+        config.register(new BindInjectMeInFeature());
+        config.register(InjectIntoFeatureClass.class);
+        config.register(new InjectIntoFeatureInstance());
+
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
+        config.configureMetaProviders(injectionManager, finalizer);
+
+        assertThat("Feature instance not injected", config.getProperty("instance-injected").toString(), is("true"));
+        assertThat("Feature class not injected", config.getProperty("class-injected").toString(), is("true"));
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/ExecutorProvidersTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/ExecutorProvidersTest.java
new file mode 100644
index 0000000..1cfd53e
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/ExecutorProvidersTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2015, 2018 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.common.process.internal;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.util.Producer;
+import org.glassfish.jersey.process.internal.ExecutorProviders;
+import org.glassfish.jersey.spi.ExecutorServiceProvider;
+import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
+import org.glassfish.jersey.spi.ScheduledThreadPoolExecutorProvider;
+import org.glassfish.jersey.spi.ThreadPoolExecutorProvider;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ExecutorProviders unit tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ExecutorProvidersTest extends AbstractBinder {
+
+    /**
+     * Custom scheduler injection qualifier.
+     */
+    @Qualifier
+    @Retention(RetentionPolicy.RUNTIME)
+    public static @interface CustomScheduler {
+
+    }
+
+    /**
+     * Custom scheduler provider.
+     */
+    @CustomScheduler
+    public static class CustomSchedulerProvider extends ScheduledThreadPoolExecutorProvider {
+
+        /**
+         * Create a new instance of the scheduled thread pool executor provider.
+         */
+        public CustomSchedulerProvider() {
+            super("custom-scheduler");
+        }
+    }
+
+    /**
+     * Custom named scheduler provider.
+     */
+    @Named("custom-scheduler")
+    public static class CustomNamedSchedulerProvider extends ScheduledThreadPoolExecutorProvider {
+
+        /**
+         * Create a new instance of the scheduled thread pool executor provider.
+         */
+        public CustomNamedSchedulerProvider() {
+            super("custom-named-scheduler");
+        }
+    }
+
+    /**
+     * Custom executor injection qualifier.
+     */
+    @Qualifier
+    @Retention(RetentionPolicy.RUNTIME)
+    public static @interface CustomExecutor {
+
+    }
+
+    /**
+     * Custom executor provider.
+     */
+    @CustomExecutor
+    public static class CustomExecutorProvider extends ThreadPoolExecutorProvider {
+
+        /**
+         * Create a new instance of the thread pool executor provider.
+         */
+        public CustomExecutorProvider() {
+            super("custom-executor");
+        }
+    }
+
+    /**
+     * Custom named executor provider.
+     */
+    @Named("custom-executor")
+    public static class CustomNamedExecutorProvider extends ThreadPoolExecutorProvider {
+
+        /**
+         * Create a new instance of the thread pool executor provider.
+         */
+        public CustomNamedExecutorProvider() {
+            super("custom-named-executor");
+        }
+    }
+
+    /**
+     * A task to retrieve the current thread name.
+     */
+    public static class CurrentThreadNameRetrieverTask implements Producer<String> {
+
+        @Override
+        public String call() {
+            return Thread.currentThread().getName();
+        }
+    }
+
+    /**
+     * Notifier of pre-destroy method invocation.
+     */
+    public static class PreDestroyNotifier {
+
+        private final CountDownLatch latch = new CountDownLatch(1);
+
+        @PreDestroy
+        public void preDestroy() {
+            latch.countDown();
+        }
+
+        public boolean await(final long timeout, final TimeUnit unit) throws InterruptedException {
+            return latch.await(timeout, unit);
+        }
+    }
+
+    /**
+     * Injectable executor client class.
+     */
+    public static class InjectedExecutorClient {
+
+        @Inject
+        private PreDestroyNotifier preDestroyNotifier;
+
+        @Inject
+        @CustomExecutor
+        private ExecutorService customExecutor;
+
+        @Inject
+        @Named("custom-executor")
+        private ExecutorService customNamedExecutor;
+
+        @Inject
+        @CustomScheduler
+        private ScheduledExecutorService customScheduler;
+
+        @Inject
+        @CustomScheduler
+        private ExecutorService customSchedulerAsExecutor;
+
+        @Inject
+        @Named("custom-scheduler")
+        private ScheduledExecutorService customNamedScheduler;
+
+        @Inject
+        @Named("custom-scheduler")
+        private ScheduledExecutorService customNamedSchedulerAsExecutor;
+
+    }
+
+    private InjectionManager injectionManager;
+
+    @Override
+    protected void configure() {
+
+
+
+        bind(CustomExecutorProvider.class).to(ExecutorServiceProvider.class).in(Singleton.class);
+        bind(CustomNamedExecutorProvider.class).to(ExecutorServiceProvider.class).in(Singleton.class);
+        bind(CustomSchedulerProvider.class).to(ScheduledExecutorServiceProvider.class).in(Singleton.class);
+        bind(CustomNamedSchedulerProvider.class).to(ScheduledExecutorServiceProvider.class).in(Singleton.class);
+        bindAsContract(PreDestroyNotifier.class).in(Singleton.class);
+    }
+
+    /**
+     * Set-up the tests.
+     */
+    @Before
+    public void setup() {
+        injectionManager = Injections.createInjectionManager(this);
+        ExecutorProviders.registerExecutorBindings(injectionManager);
+        injectionManager.completeRegistration();
+    }
+
+    /**
+     * Test executor and scheduler injection as well as the proper shutdown when injection manager is closed.
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testExecutorInjectionAndReleasing() throws Exception {
+        final InjectedExecutorClient executorClient = Injections.getOrCreate(injectionManager, InjectedExecutorClient.class);
+
+        assertThat(executorClient.customExecutor, Matchers.notNullValue());
+        assertThat(executorClient.customNamedExecutor, Matchers.notNullValue());
+
+        assertThat(executorClient.customScheduler, Matchers.notNullValue());
+        assertThat(executorClient.customNamedScheduler, Matchers.notNullValue());
+        assertThat(executorClient.customSchedulerAsExecutor, Matchers.notNullValue());
+        assertThat(executorClient.customNamedSchedulerAsExecutor, Matchers.notNullValue());
+
+        CurrentThreadNameRetrieverTask nameRetrieverTask = new CurrentThreadNameRetrieverTask();
+
+        // Test authenticity of injected executors
+        assertThat(executorClient.customExecutor.submit(nameRetrieverTask).get(),
+                Matchers.startsWith("custom-executor-"));
+        assertThat(executorClient.customNamedExecutor.submit(nameRetrieverTask).get(),
+                Matchers.startsWith("custom-named-executor-"));
+
+        // Test authenticity of injected schedulers
+        assertThat(executorClient.customScheduler.submit(nameRetrieverTask).get(),
+                Matchers.startsWith("custom-scheduler-"));
+        assertThat(executorClient.customNamedScheduler.submit(nameRetrieverTask).get(),
+                Matchers.startsWith("custom-named-scheduler-"));
+        assertThat(executorClient.customSchedulerAsExecutor.submit(nameRetrieverTask).get(),
+                Matchers.startsWith("custom-scheduler-"));
+        assertThat(executorClient.customNamedSchedulerAsExecutor.submit(nameRetrieverTask).get(),
+                Matchers.startsWith("custom-named-scheduler-"));
+
+        // Test proper executor shutdown when locator is shut down.
+        injectionManager.shutdown();
+
+        assertThat("Waiting for pre-destroy timed out.",
+                executorClient.preDestroyNotifier.await(3, TimeUnit.SECONDS), Matchers.is(true));
+
+        testShutDown("customExecutor", executorClient.customExecutor);
+        testShutDown("customNamedExecutor", executorClient.customNamedExecutor);
+        testShutDown("customScheduler", executorClient.customScheduler);
+        testShutDown("customNamedScheduler", executorClient.customNamedScheduler);
+        testShutDown("customSchedulerAsExecutor", executorClient.customSchedulerAsExecutor);
+        testShutDown("customNamedSchedulerAsExecutor", executorClient.customNamedSchedulerAsExecutor);
+    }
+
+    private void testShutDown(String name, ExecutorService executorService) throws InterruptedException {
+        assertTrue(name + " not shutdown", executorService.isShutdown());
+        assertTrue(name + " not terminated", executorService.isTerminated());
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java
new file mode 100644
index 0000000..36a23e9
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2012, 2018 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.common.process.internal;
+
+import java.lang.reflect.Type;
+
+import org.glassfish.jersey.inject.hk2.Hk2RequestScope;
+import org.glassfish.jersey.internal.inject.ForeignDescriptor;
+import org.glassfish.jersey.process.internal.RequestScope;
+
+import org.glassfish.hk2.api.ServiceHandle;
+import org.glassfish.hk2.utilities.AbstractActiveDescriptor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test of the {@link RequestScope request scope}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class RequestScopeTest {
+
+    @Test
+    public void testScopeWithCreatedInstance() {
+        final RequestScope requestScope = new Hk2RequestScope();
+        assertNull(requestScope.suspendCurrent());
+        final Hk2RequestScope.Instance context = (Hk2RequestScope.Instance) requestScope.createContext();
+        ForeignDescriptor inhab = ForeignDescriptor.wrap(new TestProvider("a"));
+        context.put(inhab, "1");
+        requestScope.runInScope(context, () -> {
+            assertEquals("1", context.get(inhab));
+            context.release();
+            assertEquals("1", context.get(inhab));
+        });
+        assertNull(context.get(inhab));
+    }
+
+    @Test
+    public void testScopeReleaseInsideScope() {
+        final RequestScope requestScope = new Hk2RequestScope();
+        assertNull(requestScope.suspendCurrent());
+        final Hk2RequestScope.Instance instance = (Hk2RequestScope.Instance) requestScope.createContext();
+        ForeignDescriptor inhab = ForeignDescriptor.wrap(new TestProvider("a"));
+        instance.put(inhab, "1");
+        requestScope.runInScope(instance, () -> {
+            final Hk2RequestScope.Instance internalInstance = (Hk2RequestScope.Instance) requestScope.suspendCurrent();
+            assertEquals(internalInstance, instance);
+            assertEquals("1", instance.get(inhab));
+            instance.release();
+            assertEquals("1", instance.get(inhab));
+        });
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertNull(instance.get(inhab));
+    }
+
+    @Test
+    public void testScopeWithImplicitInstance() throws Exception {
+        final RequestScope requestScope = new Hk2RequestScope();
+        assertNull(requestScope.suspendCurrent());
+        ForeignDescriptor inhab = ForeignDescriptor.wrap(new TestProvider("a"));
+        final Hk2RequestScope.Instance instance = requestScope.runInScope(() -> {
+            final Hk2RequestScope.Instance internalInstance = (Hk2RequestScope.Instance) requestScope.suspendCurrent();
+            assertNull(internalInstance.get(inhab));
+            internalInstance.put(inhab, "1");
+            assertEquals("1", internalInstance.get(inhab));
+            return internalInstance;
+        });
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertNull(instance.get(inhab));
+    }
+
+    @Test
+    public void testScopeWithTwoInternalTasks() throws Exception {
+        final RequestScope requestScope = new Hk2RequestScope();
+        assertNull(requestScope.suspendCurrent());
+        ForeignDescriptor inhab = ForeignDescriptor.wrap(new TestProvider("a"));
+        final Hk2RequestScope.Instance instance = requestScope.runInScope(() -> {
+            final Hk2RequestScope.Instance internalInstance = (Hk2RequestScope.Instance) requestScope.suspendCurrent();
+
+            final Hk2RequestScope.Instance anotherInstance = requestScope.runInScope(() -> {
+                final Hk2RequestScope.Instance currentInstance = (Hk2RequestScope.Instance) requestScope.suspendCurrent();
+                assertTrue(!currentInstance.equals(internalInstance));
+                currentInstance.put(inhab, "1");
+                return currentInstance;
+            });
+            assertTrue(!anotherInstance.equals(internalInstance));
+            assertEquals("1", anotherInstance.get(inhab));
+            anotherInstance.release();
+            assertNull(anotherInstance.get(inhab));
+
+            return internalInstance;
+        });
+        instance.release();
+        assertNull(instance.get(inhab));
+    }
+
+    @Test
+    public void testMultipleGetInstanceCalls() throws Exception {
+        final RequestScope requestScope = new Hk2RequestScope();
+        assertNull(requestScope.suspendCurrent());
+        ForeignDescriptor inhab = ForeignDescriptor.wrap(new TestProvider("a"));
+        final Hk2RequestScope.Instance instance = requestScope.runInScope(() -> {
+            final Hk2RequestScope.Instance internalInstance = (Hk2RequestScope.Instance) requestScope.suspendCurrent();
+            internalInstance.put(inhab, "1");
+            requestScope.suspendCurrent();
+            requestScope.suspendCurrent();
+            requestScope.suspendCurrent();
+            requestScope.suspendCurrent();
+            return internalInstance;
+        });
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertEquals("1", instance.get(inhab));
+        instance.release();
+        assertNull(instance.get(inhab));
+    }
+
+    /**
+     * Test request scope inhabitant.
+     */
+    public static class TestProvider extends AbstractActiveDescriptor<String> {
+
+        private final String id;
+
+        public TestProvider(final String id) {
+            super();
+            this.id = id;
+        }
+
+        @Override
+        public Class<?> getImplementationClass() {
+            return String.class;
+        }
+
+        @Override
+        public Type getImplementationType() {
+            return getImplementationClass();
+        }
+
+        @Override
+        public String create(final ServiceHandle<?> root) {
+            return id;
+        }
+    }
+}
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/uri/internal/JerseyUriBuilderTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/uri/internal/JerseyUriBuilderTest.java
new file mode 100644
index 0000000..7583010
--- /dev/null
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/uri/internal/JerseyUriBuilderTest.java
@@ -0,0 +1,1631 @@
+/*
+ * Copyright (c) 2011, 2018 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.common.uri.internal;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.uri.UriComponent;
+import org.glassfish.jersey.uri.internal.JerseyUriBuilder;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Uri builder implementation test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Martin Matula
+ * @author Miroslav Fuksa
+ * @author Paul Sandoz
+ * @author Vetle Leinonen-Roeim (vetle at roeim.net)
+ */
+public class JerseyUriBuilderTest {
+
+    public JerseyUriBuilderTest() {
+    }
+
+    // Reproducer for JERSEY-2537
+    @Test
+    public void shouldKeepTrailingSlash() throws MalformedURLException, URISyntaxException {
+        final URL url = new URL("http://example.com/authentications;email=joe@joe.com/");
+        final UriBuilder builder = UriBuilder.fromPath(url.getPath()).replaceMatrix(null);
+
+        final URI result = builder.build();
+        assertEquals("/authentications/", result.toString());
+    }
+
+    // Reproducer for JERSEY-2537
+    @Test
+    public void shouldRemoveAllIncludingSemicolon() throws MalformedURLException, URISyntaxException {
+        final URL url = new URL("http://example.com/authentications;email=joe@joe.com");
+        final UriBuilder builder = UriBuilder.fromPath(url.getPath()).replaceMatrix(null);
+
+        final URI result = builder.build();
+        assertEquals("/authentications", result.toString());
+    }
+
+    // Reproducer for JERSEY-2537
+    @Test
+    public void shouldLeaveURIUntouched() {
+        final UriBuilder builder = UriBuilder.fromPath("/apples;order=random;color=blue/2006").replaceMatrix(null);
+        final URI result = builder.build();
+        assertEquals("/apples;order=random;color=blue/2006", result.toString());
+    }
+
+    // Reproducer for JERSEY-2537
+    @Test
+    public void shouldLeaveURIUntouchedAndKeepSlash() {
+        final UriBuilder builder = UriBuilder.fromPath("/apples;order=random;color=blue/2006/").replaceMatrix(null);
+        final URI result = builder.build();
+        assertEquals("/apples;order=random;color=blue/2006/", result.toString());
+    }
+
+    // Reproducer for JERSEY-2537
+    @Test
+    public void shouldOnlyRemoveMatrixInFinalSegment() {
+        final UriBuilder builder = UriBuilder.fromPath("/apples;order=random;color=blue/2006/bar;zot=baz").replaceMatrix(null);
+        final URI result = builder.build();
+        assertEquals("/apples;order=random;color=blue/2006/bar", result.toString());
+    }
+
+    // Reproducer for JERSEY-2537
+    @Test
+    public void shouldOnlyRemoveMatrixInFinalSegmentAndKeepSlash() {
+        final UriBuilder builder = UriBuilder.fromPath("/apples;order=random;color=blue/2006/bar;zot=baz/").replaceMatrix(null);
+        final URI result = builder.build();
+        assertEquals("/apples;order=random;color=blue/2006/bar/", result.toString());
+    }
+
+    // Reproducer for JERSEY-2036
+    @Test
+    public void testReplaceNonAsciiQueryParam() throws UnsupportedEncodingException, MalformedURLException, URISyntaxException {
+        final URL url = new URL("http://example.com/getMyName?néme=t");
+        final String query = url.getQuery();
+
+        final UriBuilder builder = UriBuilder.fromPath(url.getPath()).scheme(url.getProtocol()).host(url.getHost())
+                .port(url.getPort())
+                .replaceQuery(query).fragment(url.getRef());
+
+        // Replace QueryParam.
+        final String parmName = "néme";
+        final String value = "value";
+
+        builder.replaceQueryParam(parmName, value);
+
+        final URI result = builder.build();
+        final URI expected = new URI("http://example.com/getMyName?néme=value");
+        assertEquals(expected.toASCIIString(), result.toASCIIString());
+    }
+
+    @Test
+    // See JAX_RS_SPEC-245
+    public void testReplacingUserInfo() {
+        final String userInfo = "foo:foo";
+
+        URI uri;
+        uri = UriBuilder.fromUri("http://foo2:foo2@localhost:8080").userInfo(userInfo).build();
+        assertEquals(userInfo, uri.getRawUserInfo());
+
+        uri = UriBuilder.fromUri("http://localhost:8080").userInfo(userInfo).build();
+        assertEquals(userInfo, uri.getRawUserInfo());
+    }
+
+    // Reproducer for JERSEY-1800
+    @Test
+    public void testEmptyUriString() throws URISyntaxException {
+        final URI uri = URI.create("");
+        JerseyUriBuilder ub = new JerseyUriBuilder().uri("news:comp.lang.java").uri(uri);
+        assertEquals("news:", ub.toTemplate());
+        // note that even though the URI is valid according to RFC 3986,
+        // it is not possible to create a java.net.URI from this builder if SSP is empty
+
+        ub = new JerseyUriBuilder().uri("news:comp.lang.java").uri("");
+        assertEquals("news:", ub.toTemplate());
+        // note that even though the URI is valid according to RFC 3986,
+        // it is not possible to create a java.net.URI from this builder if SSP is empty
+    }
+
+    // Reproducer for JERSEY-2753
+    @Test
+    public void testUriBuilderShouldLeaveRelativePathRelative() {
+        final UriBuilder builder = JerseyUriBuilder.fromPath("");
+        builder.scheme("http");
+        builder.replacePath("path");
+
+        assertEquals("http:path", builder.build().toString());
+    }
+
+    @Test
+    public void testToTemplate() throws URISyntaxException {
+        final JerseyUriBuilder ub = new JerseyUriBuilder().uri(new URI("http://examples.jersey.java.net/")).userInfo("{T1}")
+                .path("{T2}").segment("{T3}").queryParam("a", "{T4}", "v1")
+                .queryParam("b", "v2");
+        assertEquals("http://{T1}@examples.jersey.java.net/{T2}/{T3}?a={T4}&a=v1&b=v2", ub.toTemplate());
+
+        ub.queryParam("a", "v3").queryParam("c", "v4");
+        assertEquals("http://{T1}@examples.jersey.java.net/{T2}/{T3}?a={T4}&a=v1&b=v2&a=v3&c=v4", ub.toTemplate());
+    }
+
+    @Test
+    public void testPathTemplateValueEncoding() throws URISyntaxException {
+        String result;
+        result = new JerseyUriBuilder().uri(new URI("http://examples.jersey.java.net/")).userInfo("a/b").path("a/b")
+                .segment("a/b").build().toString();
+        assertEquals("http://a%2Fb@examples.jersey.java.net/a/b/a%2Fb", result);
+
+        result = new JerseyUriBuilder().uri(new URI("http://examples.jersey.java.net/")).userInfo("{T1}").path("{T2}")
+                .segment("{T3}").build("a/b", "a/b", "a/b").toString();
+        assertEquals("http://a%2Fb@examples.jersey.java.net/a%2Fb/a%2Fb", result);
+
+        result = new JerseyUriBuilder().uri(new URI("http://examples.jersey.java.net/")).userInfo("{T1}").path("{T2}")
+                .segment("{T3}").build(new Object[] {"a/b", "a/b", "a/b"}, false).toString();
+        assertEquals("http://a%2Fb@examples.jersey.java.net/a/b/a/b", result);
+
+        result = new JerseyUriBuilder().uri(new URI("http://examples.jersey.java.net/")).userInfo("{T1}").path("{T2}")
+                .segment("{T2}").build("a@b", "a@b").toString();
+        assertEquals("http://a%40b@examples.jersey.java.net/a@b/a@b", result);
+
+        result = new JerseyUriBuilder().uri(new URI("http://examples.jersey.java.net/")).userInfo("{T}").path("{T}")
+                .segment("{T}").build("a@b").toString();
+        assertEquals("http://a%40b@examples.jersey.java.net/a@b/a@b", result);
+    }
+
+    @Test
+    public void testReplaceMatrixParamWithNull() {
+        final UriBuilder builder = new JerseyUriBuilder().matrixParam("matrix", "param1", "param2");
+        builder.replaceMatrixParam("matrix", (Object[]) null);
+        assertEquals(builder.build().toString(), "");
+    }
+
+    // for completeness (added along with regression tests for JERSEY-1114)
+    @Test
+    public void testBuildNoSlashUri() {
+        final UriBuilder builder = new JerseyUriBuilder().uri(URI.create("http://localhost:8080")).path("test");
+        assertEquals("http://localhost:8080/test", builder.build().toString());
+    }
+
+    // regression test for JERSEY-1114
+    @Test
+    public void testBuildFromMapNoSlashInUri() {
+        final UriBuilder builder = new JerseyUriBuilder().uri(URI.create("http://localhost:8080")).path("test");
+        assertEquals("http://localhost:8080/test", builder.buildFromMap(new HashMap<String, Object>()).toString());
+    }
+
+    // regression test for JERSEY-1114
+    @Test
+    public void testBuildFromArrayNoSlashInUri() {
+        final UriBuilder builder = new JerseyUriBuilder().uri(URI.create("http://localhost:8080")).path("test");
+        assertEquals("http://localhost:8080/test", builder.build("testing").toString());
+    }
+
+    @Test
+    public void testReplaceNullMatrixParam() {
+        try {
+            new JerseyUriBuilder().replaceMatrixParam(null, "param");
+        } catch (final IllegalArgumentException e) {
+            return;
+        } catch (final Exception e) {
+            fail("Expected IllegalArgumentException but got " + e.toString());
+        }
+        fail("Expected IllegalArgumentException but no exception was thrown.");
+    }
+
+    // regression test for JERSEY-1081
+    @Test
+    public void testReplaceQueryParam() {
+        final URI uri = new JerseyUriBuilder().path("http://localhost/").replaceQueryParam("foo", "test").build();
+        assertEquals("http://localhost/?foo=test", uri.toString());
+    }
+
+    // regression test for JERSEY-1081
+    @Test
+    public void testReplaceQueryParamAndClone() {
+        final URI uri = new JerseyUriBuilder().path("http://localhost/").replaceQueryParam("foo", "test").clone().build();
+        assertEquals("http://localhost/?foo=test", uri.toString());
+    }
+
+    // regression test for JERSEY-1341
+    @Test
+    public void testEmptyQueryParamValue() {
+        final URI uri = new JerseyUriBuilder().path("http://localhost/").queryParam("test", "").build();
+        assertEquals("http://localhost/?test=", uri.toString());
+    }
+
+    // regression test for JERSEY-1457
+    @Test
+    public void testChangeSspViaStringUriTemplate() throws Exception {
+        final String[] origUris = new String[] {"news:comp.lang.java", "tel:+1-816-555-1212"};
+        final URI[] replaceUris = new URI[] {new URI(null, "news.lang.java", null), new URI(null, "+1-866-555-1212", null)};
+        final String[] results = new String[] {"news:news.lang.java", "tel:+1-866-555-1212"};
+        int i = 0;
+        while (i < origUris.length) {
+            assertEquals(results[i],
+                    UriBuilder.fromUri(new URI(origUris[i])).uri(replaceUris[i].toASCIIString()).build().toString());
+            i++;
+        }
+    }
+
+    @Test
+    public void testChangeUriStringAfterChangingOpaqueSchemeToHttp() {
+        assertEquals("http://www.example.org/test",
+                UriBuilder.fromUri("tel:+1-816-555-1212").scheme("http").uri("//www.{host}.org").path("test").build("example")
+                        .toString());
+    }
+
+    @Test
+    public void testUriBuilderTemplatesSimple() {
+        testUri("a:/path");
+        testUri("a:/p");
+        testUri("a:/path/x/y/z");
+        testUri("a:/path/x?q=12#fragment");
+        testUri("a:/p?q#f");
+        testUri("a://host");
+        testUri("a://host:5555/a/b");
+        testUri("a://h:5/a/b");
+        testUri("a:/user@host:12345");         //user@host:12345 is not authority but path
+        testUri("a:/user@host:12345/a/b/c");
+        testUri("a:/user@host:12345/a/b/c?aaa&bbb#ccc");
+        testUri("a:/user@host.hhh.ddd.c:12345/a/b/c?aaa&bbb#ccc");
+        testUri("/a");
+        testUri("/a/../../b/c/d");
+        testUri("//localhost:80/a/b");
+        testUri("//l:8/a/b");
+        testUri("a/b");
+        testUri("a");
+        testUri("../../s");
+        testUri("mailto:test@test.com");
+        testUri("http://orac@le:co@m:1234/a/b/ccc?a#fr");
+        testUri("http://[::FFFF:129.144.52.38]:1234/a/b/ccc?a#fr");
+        testUri("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:1234/a/b/ccc?a#fr");
+
+    }
+
+    @Test
+    @Ignore
+    public void failingTests() {
+        testUri("a://#fragment"); // fails in JerseyUriBuilder
+        testUri("a://?query");
+
+        // fails: opaque uris are not supported by UriTemplate
+        final URI uri = new JerseyUriBuilder().uri("{scheme}://{mailto}").build("mailto", "email@test.ttt");
+        assertEquals("mailto:email@test.ttt", uri.toString());
+    }
+
+    @Test
+    public void testUriBuilderTemplates() {
+        URI uri = new JerseyUriBuilder().uri("http://localhost:8080/{path}").build("a/b/c");
+        assertEquals("http://localhost:8080/a%2Fb%2Fc", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("{scheme}://{host}").build("http", "localhost");
+        assertEquals("http://localhost", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("http://{host}:8080/{path}").build("l", "a/b/c");
+        assertEquals("http://l:8080/a%2Fb%2Fc", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("{scheme}://{host}:{port}/{path}").build("s", "h", new Integer(1), "a");
+        assertEquals("s://h:1/a", uri.toString());
+
+        final Map<String, Object> values = new HashMap<String, Object>();
+        values.put("scheme", "s");
+        values.put("host", "h");
+        values.put("port", 1);
+        values.put("path", "p/p");
+        values.put("query", "q");
+        values.put("fragment", "f");
+
+        uri = new JerseyUriBuilder().uri("{scheme}://{host}:{port}/{path}?{query}#{fragment}").buildFromMap(values);
+        assertEquals("s://h:1/p%2Fp?q#f", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("{scheme}://{host}:{port}/{path}/{path2}").build("s", "h", new Integer(1), "a", "b");
+        assertEquals("s://h:1/a/b", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("{scheme}://{host}:{port}/{path}/{path2}").build("s", "h", new Integer(1), "a", "b");
+        assertEquals("s://h:1/a/b", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("//{host}:{port}/{path}/{path2}").build("h", new Integer(1), "a", "b");
+        assertEquals("//h:1/a/b", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("/{a}/{a}/{b}").build("a", "b");
+        assertEquals("/a/a/b", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("/{a}/{a}/{b}?{queryParam}").build("a", "b", "query");
+        assertEquals("/a/a/b?query", uri.toString());
+
+        // partial templates
+        uri = new JerseyUriBuilder().uri("/{a}xx/{a}/{b}?{queryParam}").build("a", "b", "query");
+        assertEquals("/axx/a/b?query", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("my{scheme}://my{host}:1{port}/my{path}/my{path2}")
+                .build("s", "h", new Integer(1), "a", "b/c");
+        assertEquals("mys://myh:11/mya/myb%2Fc", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("my{scheme}post://my{host}post:5{port}9/my{path}post/my{path2}post")
+                .build("s", "h", new Integer(1), "a", "b");
+        assertEquals("myspost://myhpost:519/myapost/mybpost", uri.toString());
+    }
+
+    @Test
+    public void testUriBuilderTemplatesNotEncodedSlash() {
+        URI uri = new JerseyUriBuilder().uri("http://localhost:8080/{path}").build(new Object[] {"a/b/c"}, false);
+        assertEquals("http://localhost:8080/a/b/c", uri.toString());
+
+        uri = new JerseyUriBuilder().uri("http://{host}:8080/{path}").build(new Object[] {"l", "a/b/c"}, false);
+        assertEquals("http://l:8080/a/b/c", uri.toString());
+
+        final Map<String, Object> values = new HashMap<String, Object>();
+        values.put("scheme", "s");
+        values.put("host", "h");
+        values.put("port", 1);
+        values.put("path", "p/p");
+        values.put("query", "q");
+        values.put("fragment", "f");
+
+        uri = new JerseyUriBuilder().uri("{scheme}://{host}:{port}/{path}?{query}#{fragment}").buildFromMap(values, false);
+        assertEquals("s://h:1/p/p?q#f", uri.toString());
+    }
+
+    private void testUri(final String input) {
+        final URI uri = new JerseyUriBuilder().uri(input).clone().build();
+
+        final URI originalUri = URI.create(input);
+        assertEquals(originalUri.getScheme(), uri.getScheme());
+        assertEquals(originalUri.getHost(), uri.getHost());
+        assertEquals(originalUri.getPort(), uri.getPort());
+        assertEquals(originalUri.getUserInfo(), uri.getUserInfo());
+        assertEquals(originalUri.getPath(), uri.getPath());
+        assertEquals(originalUri.getQuery(), uri.getQuery());
+        assertEquals(originalUri.getFragment(), uri.getFragment());
+        assertEquals(originalUri.getRawSchemeSpecificPart(), uri.getRawSchemeSpecificPart());
+        assertEquals(originalUri.isAbsolute(), uri.isAbsolute());
+        assertEquals(input, uri.toString());
+    }
+
+    @org.junit.Test
+    public void testOpaqueUri() {
+        final URI uri = UriBuilder.fromUri("mailto:a@b").build();
+        Assert.assertEquals("mailto:a@b", uri.toString());
+    }
+
+    @Test
+    public void testOpaqueUriReplaceSchemeSpecificPart() {
+        final URI uri = UriBuilder.fromUri("mailto:a@b").schemeSpecificPart("c@d").build();
+        Assert.assertEquals("mailto:c@d", uri.toString());
+    }
+
+    @Test
+    public void testOpaqueReplaceUri() {
+        final URI uri = UriBuilder.fromUri("mailto:a@b").uri(URI.create("c@d")).build();
+        Assert.assertEquals("mailto:c@d", uri.toString());
+    }
+
+    @Test
+    public void testReplaceScheme() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").scheme("https").build();
+        Assert.assertEquals("https://localhost:8080/a/b/c", uri.toString());
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").scheme(null).build();
+        Assert.assertEquals("//localhost:8080/a/b/c", uri.toString());
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").scheme(null).host(null).build();
+        Assert.assertEquals("//:8080/a/b/c", uri.toString());
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").scheme(null).host(null).port(-1).build();
+        Assert.assertEquals("/a/b/c", uri.toString());
+    }
+
+    @Test
+    public void testReplaceSchemeSpecificPart() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").schemeSpecificPart("//localhost:8080/a/b/c/d").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/d"), uri);
+    }
+
+    @Test
+    public void testNameAuthorityUri() {
+        final URI uri = UriBuilder.fromUri("http://x_y/a/b/c").build();
+        Assert.assertEquals(URI.create("http://x_y/a/b/c"), uri);
+    }
+
+    @Test
+    public void testReplaceNameAuthorityUriWithHost() {
+        final URI uri = UriBuilder.fromUri("http://x_y.com/a/b/c").host("xy.com").build();
+        Assert.assertEquals(URI.create("http://xy.com/a/b/c"), uri);
+    }
+
+    @Test
+    public void testReplaceNameAuthorityUriWithSSP() {
+        URI uri = UriBuilder.fromUri("http://x_y.com/a/b/c").schemeSpecificPart("//xy.com/a/b/c").build();
+        Assert.assertEquals(URI.create("http://xy.com/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://x_y.com/a/b/c").schemeSpecificPart("//v_w.com/a/b/c").build();
+        Assert.assertEquals(URI.create("http://v_w.com/a/b/c"), uri);
+    }
+
+    @Test
+    public void testReplaceUserInfo() {
+        final URI uri = UriBuilder.fromUri("http://bob@localhost:8080/a/b/c").userInfo("sue").build();
+        Assert.assertEquals(URI.create("http://sue@localhost:8080/a/b/c"), uri);
+    }
+
+    @Test
+    public void testReplaceHost() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").host("a.com").build();
+        Assert.assertEquals(URI.create("http://a.com:8080/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").host("[::FFFF:129.144.52.38]").build();
+        Assert.assertEquals(URI.create("http://[::FFFF:129.144.52.38]:8080/a/b/c"), uri);
+    }
+
+    @Test
+    public void testReplacePort() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").port(9090).build();
+        Assert.assertEquals(URI.create("http://localhost:9090/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").port(-1).build();
+        Assert.assertEquals(URI.create("http://localhost/a/b/c"), uri);
+    }
+
+    @Test
+    public void testReplacePath() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").replacePath("/x/y/z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/x/y/z"), uri);
+    }
+
+    @Test
+    public void testReplacePathNull() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").replacePath(null).build();
+
+        Assert.assertEquals(URI.create("http://localhost:8080"), uri);
+    }
+
+    @Test
+    public void testReplaceMatrix() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c;a=x;b=y").replaceMatrix("x=a;y=b").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c;x=a;y=b"), uri);
+    }
+
+    @Test
+    public void testReplaceMatrixParams() {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost:8080/a/b/c;a=x;b=y").replaceMatrixParam("a", "z", "zz");
+
+        {
+            final URI uri = ubu.build();
+            final List<PathSegment> ps = UriComponent.decodePath(uri, true);
+            final MultivaluedMap<String, String> mps = ps.get(2).getMatrixParameters();
+            final List<String> a = mps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("z", a.get(0));
+            Assert.assertEquals("zz", a.get(1));
+            final List<String> b = mps.get("b");
+            Assert.assertEquals(1, b.size());
+            Assert.assertEquals("y", b.get(0));
+        }
+
+        {
+            final URI uri = ubu.replaceMatrixParam("a", "_z_", "_zz_").build();
+            final List<PathSegment> ps = UriComponent.decodePath(uri, true);
+            final MultivaluedMap<String, String> mps = ps.get(2).getMatrixParameters();
+            final List<String> a = mps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("_z_", a.get(0));
+            Assert.assertEquals("_zz_", a.get(1));
+            final List<String> b = mps.get("b");
+            Assert.assertEquals(1, b.size());
+            Assert.assertEquals("y", b.get(0));
+        }
+
+        {
+            final URI uri = JerseyUriBuilder.fromUri("http://localhost:8080/a/b/c;a=x;b=y").replaceMatrixParam("a", "z", "zz")
+                    .matrixParam("c", "c").path(
+                            "d").build();
+
+            final List<PathSegment> ps = UriComponent.decodePath(uri, true);
+            final MultivaluedMap<String, String> mps = ps.get(2).getMatrixParameters();
+            final List<String> a = mps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("z", a.get(0));
+            Assert.assertEquals("zz", a.get(1));
+            final List<String> b = mps.get("b");
+            Assert.assertEquals(1, b.size());
+            Assert.assertEquals("y", b.get(0));
+            final List<String> c = mps.get("c");
+            Assert.assertEquals(1, c.size());
+            Assert.assertEquals("c", c.get(0));
+        }
+
+        {
+            final URI uri = JerseyUriBuilder.fromUri("http://localhost:8080/a;w=123;q=15/b/c;a=x;b=y").replaceMatrixParam("a",
+                    "z", "zz").matrixParam("c", "c").path("d").build();
+
+            final List<PathSegment> ps = UriComponent.decodePath(uri, true);
+            MultivaluedMap<String, String> mps = ps.get(0).getMatrixParameters();
+
+            List<String> w = mps.get("w");
+            Assert.assertEquals(1, w.size());
+            Assert.assertEquals("123", w.get(0));
+
+            w = mps.get("q");
+            Assert.assertEquals(1, w.size());
+            Assert.assertEquals("15", w.get(0));
+
+            mps = ps.get(2).getMatrixParameters();
+            final List<String> a = mps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("z", a.get(0));
+            Assert.assertEquals("zz", a.get(1));
+            final List<String> b = mps.get("b");
+            Assert.assertEquals(1, b.size());
+            Assert.assertEquals("y", b.get(0));
+            final List<String> c = mps.get("c");
+            Assert.assertEquals(1, c.size());
+            Assert.assertEquals("c", c.get(0));
+        }
+    }
+
+    @Test
+    public void testReplaceMatrixParamsEmpty() {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost:8080/a/b/c").replaceMatrixParam("a", "z", "zz");
+        {
+            final URI uri = ubu.build();
+            final List<PathSegment> ps = UriComponent.decodePath(uri, true);
+            final MultivaluedMap<String, String> mps = ps.get(2).getMatrixParameters();
+            final List<String> a = mps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("z", a.get(0));
+            Assert.assertEquals("zz", a.get(1));
+        }
+    }
+
+    @Test
+    public void testReplaceMatrixParamsEncoded() throws URISyntaxException {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost/").replaceMatrix("limit=10;sql=select+*+from+users");
+        ubu.replaceMatrixParam("limit", 100);
+
+        final URI uri = ubu.build();
+        Assert.assertEquals(URI.create("http://localhost/;limit=100;sql=select+*+from+users"), uri);
+    }
+
+    @Test
+    public void testMatrixParamsWithTheSameName() {
+        UriBuilder first = UriBuilder.fromUri("http://www.com/").replaceMatrixParam("example", "one", "two");
+        first = first.path("/child");
+        first = first.replaceMatrixParam("example", "another");
+
+        Assert.assertEquals("http://www.com/;example=one;example=two/child;example=another", first.build().toString());
+    }
+
+    @Test
+    public void testMatrixParamsWithTheDifferentName() {
+        UriBuilder first = UriBuilder.fromUri("http://www.com/").replaceMatrixParam("example", "one", "two");
+        first = first.path("/child");
+        first = first.replaceMatrixParam("other", "another");
+
+        Assert.assertEquals("http://www.com/;example=one;example=two/child;other=another", first.build().toString());
+    }
+
+    @Test
+    public void testReplaceQuery() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c?a=x&b=y").replaceQuery("x=a&y=b").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?x=a&y=b"), uri);
+    }
+
+    @Test
+    public void testBuildEncodedQuery() {
+        URI u = UriBuilder.fromPath("").queryParam("y", "1 %2B 2").build();
+        Assert.assertEquals(URI.create("?y=1+%2B+2"), u);
+
+        // Issue 216
+        u = UriBuilder.fromPath("http://localhost:8080").path("/{x}/{y}/{z}/{x}").buildFromEncoded("%xy", " ", "=");
+        Assert.assertEquals(URI.create("http://localhost:8080/%25xy/%20/=/%25xy"), u);
+    }
+
+    @Test
+    public void testReplaceQueryParams() {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost:8080/a/b/c?a=x&b=y").replaceQueryParam("a", "z", "zz")
+                .queryParam("c", "c");
+
+        {
+            final URI uri = ubu.build();
+
+            final MultivaluedMap<String, String> qps = UriComponent.decodeQuery(uri, true);
+            final List<String> a = qps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("z", a.get(0));
+            Assert.assertEquals("zz", a.get(1));
+            final List<String> b = qps.get("b");
+            Assert.assertEquals(1, b.size());
+            Assert.assertEquals("y", b.get(0));
+            final List<String> c = qps.get("c");
+            Assert.assertEquals(1, c.size());
+            Assert.assertEquals("c", c.get(0));
+        }
+
+        {
+            final URI uri = ubu.replaceQueryParam("a", "_z_", "_zz_").build();
+
+            final MultivaluedMap<String, String> qps = UriComponent.decodeQuery(uri, true);
+            final List<String> a = qps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("_z_", a.get(0));
+            Assert.assertEquals("_zz_", a.get(1));
+            final List<String> b = qps.get("b");
+            Assert.assertEquals(1, b.size());
+            Assert.assertEquals("y", b.get(0));
+            final List<String> c = qps.get("c");
+            Assert.assertEquals(1, c.size());
+            Assert.assertEquals("c", c.get(0));
+        }
+
+        // issue 257 - param is removed after setting it to null
+        {
+            final URI u1 = UriBuilder.fromPath("http://localhost:8080").queryParam("x", "10")
+                    .replaceQueryParam("x", (Object[]) null)
+                    .build();
+            Assert.assertTrue(u1.toString().equals("http://localhost:8080"));
+
+            final URI u2 = UriBuilder.fromPath("http://localhost:8080").queryParam("x", "10").replaceQueryParam("x").build();
+            Assert.assertTrue(u2.toString().equals("http://localhost:8080"));
+        }
+
+        // issue 257 - IllegalArgumentException
+        {
+            boolean caught = false;
+
+            try {
+                UriBuilder.fromPath("http://localhost:8080").queryParam("x", "10").replaceQueryParam("x", "1", null, "2").build();
+            } catch (final IllegalArgumentException iae) {
+                caught = true;
+            }
+
+            Assert.assertTrue(caught);
+        }
+
+    }
+
+    @Test
+    public void testReplaceQueryParamsEmpty() {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost:8080/a/b/c").replaceQueryParam("a", "z", "zz")
+                .queryParam("c", "c");
+
+        {
+            final URI uri = ubu.build();
+
+            final MultivaluedMap<String, String> qps = UriComponent.decodeQuery(uri, true);
+            final List<String> a = qps.get("a");
+            Assert.assertEquals(2, a.size());
+            Assert.assertEquals("z", a.get(0));
+            Assert.assertEquals("zz", a.get(1));
+            final List<String> c = qps.get("c");
+            Assert.assertEquals(1, c.size());
+            Assert.assertEquals("c", c.get(0));
+        }
+    }
+
+    @Test
+    public void testReplaceQueryParamsEncoded1() throws URISyntaxException {
+        final UriBuilder ubu = UriBuilder.fromUri(new URI("http://localhost/")).replaceQuery("limit=10&sql=select+*+from+users");
+        ubu.replaceQueryParam("limit", 100);
+
+        final URI uri = ubu.build();
+        Assert.assertEquals(URI.create("http://localhost/?limit=100&sql=select+%2A+from+users"), uri);
+    }
+
+    @Test
+    public void testReplaceQueryParamsEncoded2() throws URISyntaxException {
+        final UriBuilder ubu = UriBuilder.fromUri(new URI("http://localhost")).replaceQuery("limit=10&sql=select+*+from+users");
+        ubu.replaceQueryParam("limit", 100);
+
+        final URI uri = ubu.build();
+        Assert.assertEquals(URI.create("http://localhost/?limit=100&sql=select+%2A+from+users"), uri);
+    }
+
+    @Test
+    public void testReplaceQueryParamsEncoded3() throws URISyntaxException {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost/").replaceQuery("limit=10&sql=select+*+from+users");
+        ubu.replaceQueryParam("limit", 100);
+
+        final URI uri = ubu.build();
+        Assert.assertEquals(URI.create("http://localhost/?limit=100&sql=select+%2A+from+users"), uri);
+    }
+
+    @Test
+    public void testReplaceQueryParamsEncoded4() throws URISyntaxException {
+        final UriBuilder ubu = UriBuilder.fromUri("http://localhost").replaceQuery("limit=10&sql=select+*+from+users");
+        ubu.replaceQueryParam("limit", 100);
+
+        final URI uri = ubu.build();
+        Assert.assertEquals(URI.create("http://localhost/?limit=100&sql=select+%2A+from+users"), uri);
+    }
+
+    @Test
+    public void testReplaceFragment() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c?a=x&b=y#frag").fragment("ment").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=x&b=y#ment"), uri);
+    }
+
+    @Test
+    public void testReplaceUri() {
+        final URI u = URI.create("http://bob@localhost:8080/a/b/c?a=x&b=y#frag");
+
+        URI uri = UriBuilder.fromUri(u).uri(URI.create("https://bob@localhost:8080")).build();
+        Assert.assertEquals(URI.create("https://bob@localhost:8080/a/b/c?a=x&b=y#frag"), uri);
+
+        uri = UriBuilder.fromUri(u).uri(URI.create("https://sue@localhost:8080")).build();
+        Assert.assertEquals(URI.create("https://sue@localhost:8080/a/b/c?a=x&b=y#frag"), uri);
+
+        uri = UriBuilder.fromUri(u).uri(URI.create("https://sue@localhost:9090")).build();
+        Assert.assertEquals(URI.create("https://sue@localhost:9090/a/b/c?a=x&b=y#frag"), uri);
+
+        uri = UriBuilder.fromUri(u).uri(URI.create("/x/y/z")).build();
+        Assert.assertEquals(URI.create("http://bob@localhost:8080/x/y/z?a=x&b=y#frag"), uri);
+
+        uri = UriBuilder.fromUri(u).uri(URI.create("?x=a&b=y")).build();
+        Assert.assertEquals(URI.create("http://bob@localhost:8080/a/b/c?x=a&b=y#frag"), uri);
+
+        uri = UriBuilder.fromUri(u).uri(URI.create("#ment")).build();
+        Assert.assertEquals(URI.create("http://bob@localhost:8080/a/b/c?a=x&b=y#ment"), uri);
+    }
+
+    @Test
+    public void testSchemeSpecificPart() {
+        final URI u = URI.create("http://bob@localhost:8080/a/b/c?a=x&b=y#frag");
+
+        final URI uri = UriBuilder.fromUri(u).schemeSpecificPart("//sue@remotehost:9090/x/y/z?x=a&y=b").build();
+        Assert.assertEquals(URI.create("http://sue@remotehost:9090/x/y/z?x=a&y=b#frag"), uri);
+    }
+
+    @Test
+    public void testAppendPath() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080").path("a/b/c").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/").path("a/b/c").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080").path("/a/b/c").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c/").path("/").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c/").path("/x/y/z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/x/y/z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("x/y/z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a%20/b%20/c%20").path("/x /y /z ").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a%20/b%20/c%20/x%20/y%20/z%20"), uri);
+    }
+
+    @Test
+    public void testAppendSegment() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080").segment("a/b/c;x").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a%2Fb%2Fc%3Bx"), uri);
+    }
+
+    @Test
+    public void testWhitespacesInPathParams() {
+        final URI uri = UriBuilder.fromUri("http://localhost:80/aaa/{  par1}/").path("bbb/{  par2   }/ccc")
+                .build("1param", "2param");
+        assertEquals(URI.create("http://localhost:80/aaa/1param/bbb/2param/ccc"), uri);
+    }
+
+    @Test
+    public void testWhitespacesInPathParamsByResolve() {
+        final URI uri = UriBuilder.fromUri("http://localhost:80/aaa/{  par1}/").path("bbb/{  par2   }/ccc")
+                .build("1param", "2param");
+        assertEquals(URI.create("http://localhost:80/aaa/1param/bbb/2param/ccc"), uri);
+    }
+
+    @Test
+    public void testWhitespacesInPathParams2() {
+        final URI uri = UriBuilder.fromUri("http://localhost:80/aaa/{  par1}").path("bbb/{  par2 : \\d*  }/ccc")
+                .build("1param", "2");
+        assertEquals(URI.create("http://localhost:80/aaa/1param/bbb/2/ccc"), uri);
+    }
+
+    @Test
+    public void testWhitespacesInPathParams2ByResolve() {
+        final URI uri = UriBuilder.fromUri("http://localhost:80/aaa/{  par1}").path("bbb/{  par2 : \\d*  }/ccc")
+                .resolveTemplate("par1", "1param").resolveTemplate("par2", "2").build();
+        assertEquals(URI.create("http://localhost:80/aaa/1param/bbb/2/ccc"), uri);
+    }
+
+    @Test
+    public void testWhitespacesInQueryParams() {
+        final URI uri = UriBuilder.fromUri("http://localhost:80/aaa?a={      param   : \\.d*  }").build("5");
+        assertEquals(URI.create("http://localhost:80/aaa?a=5"), uri);
+    }
+
+    @Test
+    public void testWhitespacesInQueryParamsByResolve() {
+        final URI uri = UriBuilder.fromUri("http://localhost:80/aaa?a={      param   : \\.d*  }").resolveTemplate("param", "5")
+                .build();
+        assertEquals(URI.create("http://localhost:80/aaa?a=5"), uri);
+    }
+
+    @Test
+    public void testRelativeFromUri() {
+        URI uri = UriBuilder.fromUri("a/b/c").build();
+        Assert.assertEquals(URI.create("a/b/c"), uri);
+
+        uri = UriBuilder.fromUri("a/b/c").path("d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromUri("a/b/c/").path("d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromUri("a/b/c").path("/d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromUri("a/b/c/").path("/d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromUri("").queryParam("x", "y").build();
+        Assert.assertEquals(URI.create("?x=y"), uri);
+
+    }
+
+    @Test
+    public void testRelativefromPath() {
+        URI uri = UriBuilder.fromPath("a/b/c").build();
+        Assert.assertEquals(URI.create("a/b/c"), uri);
+
+        uri = UriBuilder.fromPath("a/b/c").path("d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromPath("a/b/c/").path("d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromPath("a/b/c").path("/d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromPath("a/b/c/").path("/d").build();
+        Assert.assertEquals(URI.create("a/b/c/d"), uri);
+
+        uri = UriBuilder.fromPath("").queryParam("x", "y").build();
+        Assert.assertEquals(URI.create("?x=y"), uri);
+    }
+
+    @Test
+    public void testAppendQueryParams() throws URISyntaxException {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c?a=x&b=y").queryParam("c", "z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=x&b=y&c=z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c?a=x&b=y").queryParam("c= ", "z= ").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=x&b=y&c%3D+=z%3D+"), uri);
+
+        uri = UriBuilder.fromUri(new URI("http://localhost:8080/")).queryParam("c", "z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/?c=z"), uri);
+
+        uri = UriBuilder.fromUri(new URI("http://localhost:8080")).queryParam("c", "z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/?c=z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/").queryParam("c", "z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/?c=z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080").queryParam("c", "z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/?c=z"), uri);
+
+        try {
+            UriBuilder.fromPath("http://localhost:8080").queryParam("name", "x", null).build();
+            fail("IllegalArgumentException expected.");
+        } catch (final IllegalArgumentException e) {
+            // exception expected, move on...
+        }
+    }
+
+    @Test
+    public void testAppendMatrixParams() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c;a=x;b=y").matrixParam("c", "z").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c;a=x;b=y;c=z"), uri);
+
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c;a=x;b=y").matrixParam("c=/ ;", "z=/ ;").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c;a=x;b=y;c%3D%2F%20%3B=z%3D%2F%20%3B"), uri);
+    }
+
+    @Test
+    public void testAppendPathAndMatrixParams() {
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/").path(
+                "a").matrixParam("x", "foo").matrixParam("y", "bar").path("b").matrixParam("x", "foo").matrixParam("y", "bar")
+                .build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a;x=foo;y=bar/b;x=foo;y=bar"), uri);
+    }
+
+    @Path("resource")
+    class Resource {
+
+        @GET
+        @Path("method")
+        public String get() {
+            return "";
+        }
+
+        @Path("locator")
+        public Object locator() {
+            return null;
+        }
+    }
+
+    @Test
+    public void testResourceAppendPath() throws NoSuchMethodException {
+        URI ub = UriBuilder.fromUri("http://localhost:8080/base").path(Resource.class).build();
+        Assert.assertEquals(URI.create("http://localhost:8080/base/resource"), ub);
+
+        ub = UriBuilder.fromUri("http://localhost:8080/base").path(Resource.class, "get").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/base/method"), ub);
+
+        final Method get = Resource.class.getMethod("get");
+        final Method locator = Resource.class.getMethod("locator");
+        ub = UriBuilder.fromUri("http://localhost:8080/base").path(get).path(locator).build();
+        Assert.assertEquals(URI.create("http://localhost:8080/base/method/locator"), ub);
+    }
+
+    @Path("resource/{id}")
+    class ResourceWithTemplate {
+
+        @GET
+        @Path("method/{id1}")
+        public String get() {
+            return "";
+        }
+
+        @Path("locator/{id2}")
+        public Object locator() {
+            return null;
+        }
+    }
+
+    @Test
+    public void testResourceWithTemplateAppendPath() throws NoSuchMethodException {
+        URI ub = UriBuilder.fromUri("http://localhost:8080/base").path(ResourceWithTemplate.class).build("foo");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/resource/foo"), ub);
+
+        ub = UriBuilder.fromUri("http://localhost:8080/base").path(ResourceWithTemplate.class, "get").build("foo");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/method/foo"), ub);
+
+        final Method get = ResourceWithTemplate.class.getMethod("get");
+        final Method locator = ResourceWithTemplate.class.getMethod("locator");
+        ub = UriBuilder.fromUri("http://localhost:8080/base").path(get).path(locator).build("foo", "bar");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/method/foo/locator/bar"), ub);
+    }
+
+    @Path("resource/{id: .+}")
+    class ResourceWithTemplateRegex {
+
+        @GET
+        @Path("method/{id1: .+}")
+        public String get() {
+            return "";
+        }
+
+        @Path("locator/{id2: .+}")
+        public Object locator() {
+            return null;
+        }
+    }
+
+    @Test
+    public void testResourceWithTemplateRegexAppendPath() throws NoSuchMethodException {
+        URI ub = UriBuilder.fromUri("http://localhost:8080/base").path(ResourceWithTemplateRegex.class).build("foo");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/resource/foo"), ub);
+
+        ub = UriBuilder.fromUri("http://localhost:8080/base").path(ResourceWithTemplateRegex.class, "get").build("foo");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/method/foo"), ub);
+
+        final Method get = ResourceWithTemplateRegex.class.getMethod("get");
+        final Method locator = ResourceWithTemplateRegex.class.getMethod("locator");
+        ub = UriBuilder.fromUri("http://localhost:8080/base").path(get).path(locator).build("foo", "bar");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/method/foo/locator/bar"), ub);
+    }
+
+    interface GenericInterface<T, U> {
+
+        T find(U u);
+    }
+
+    @Path("resource/")
+    class ResourceWithGenericInterface implements GenericInterface<Object, String> {
+
+        @GET
+        @Path("{id}")
+        @Override
+        public Object find(@PathParam("id") final String s) {
+            return null;
+        }
+    }
+
+    @Test
+    public void testResourceWithGenericInterfaceAppendPath() {
+        final URI ub = UriBuilder.fromUri("http://localhost:8080/base").path(ResourceWithGenericInterface.class, "find")
+                .build("foo");
+        Assert.assertEquals(URI.create("http://localhost:8080/base/foo"), ub);
+    }
+
+    @Test
+    public void testBuildTemplates() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").build("x", "y", "z");
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").buildFromMap(m);
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x"), uri);
+    }
+
+    @Test
+    public void testBuildTemplatesByResolve() {
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+
+        final URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").resolveTemplates(m)
+                .build();
+
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x"), uri);
+    }
+
+    @Test
+    public void testBuildTemplatesWithNameAuthority() {
+        URI uri = UriBuilder.fromUri("http://x_y.com:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").build("x", "y", "z");
+        Assert.assertEquals(URI.create("http://x_y.com:8080/a/b/c/x/y/z/x"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        uri = UriBuilder.fromUri("http://x_y.com:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").buildFromMap(m);
+        Assert.assertEquals(URI.create("http://x_y.com:8080/a/b/c/x/y/z/x"), uri);
+    }
+
+    @Test
+    public void testBuildTemplatesWithNameAuthorityByResolve() {
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        final URI uri = UriBuilder.fromUri("http://x_y.com:8080/a/b/c")
+                .path("/{foo}/{bar}/{baz}/{foo}").buildFromMap(m);
+        Assert.assertEquals(URI.create("http://x_y.com:8080/a/b/c/x/y/z/x"), uri);
+    }
+
+    @Test
+    public void testBuildFromMap() {
+        final Map<String, Object> maps = new HashMap<String, Object>();
+        maps.put("x", null);
+        maps.put("y", "/path-absolute/test1");
+        maps.put("z", "fred@example.com");
+        maps.put("w", "path-rootless/test2");
+        maps.put("u", "extra");
+
+        boolean caught = false;
+
+        try {
+            System.out.println(UriBuilder.fromPath("").path("{w}/{x}/{y}/{z}/{x}").buildFromEncodedMap(maps));
+
+        } catch (final IllegalArgumentException ex) {
+            caught = true;
+        }
+
+        Assert.assertTrue(caught);
+    }
+
+    @Test
+    public void testBuildFromMapByResolve() {
+        final Map<String, Object> maps = new HashMap<String, Object>();
+        maps.put("x", null);
+        maps.put("y", "/path-absolute/test1");
+        maps.put("z", "fred@example.com");
+        maps.put("w", "path-rootless/test2");
+        maps.put("u", "extra");
+
+        boolean caught = false;
+
+        try {
+            System.out.println(UriBuilder.fromPath("").path("{w}/{x}/{y}/{z}/{x}").resolveTemplates(maps).build());
+
+        } catch (final IllegalArgumentException ex) {
+            caught = true;
+        }
+
+        Assert.assertTrue(caught);
+    }
+
+    @Test
+    public void testBuildQueryTemplates() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").queryParam("a", "{b}").build("=+&%xx%20");
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=%3D%2B%26%25xx%2520"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("b", "=+&%xx%20");
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").queryParam("a", "{b}").buildFromMap(m);
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=%3D%2B%26%25xx%2520"), uri);
+    }
+
+    @Test
+    public void testBuildFromEncodedQueryTemplates() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").queryParam("a", "{b}").buildFromEncoded("=+&%xx%20");
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=%3D%2B%26%25xx%20"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("b", "=+&%xx%20");
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").queryParam("a", "{b}").buildFromEncodedMap(m);
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=%3D%2B%26%25xx%20"), uri);
+    }
+
+    @Test
+    public void testBuildFromEncodedSlashInParamValue() {
+        assertEquals("/A/B", UriBuilder.fromUri("/{param}").buildFromEncoded("A/B").toString());
+    }
+
+    @Test
+    public void testResolveTemplateFromEncodedQueryTemplates() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").queryParam("a", "{b}")
+                .resolveTemplateFromEncoded("b", "=+&%xx%20").build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=%3D%2B%26%25xx%20"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("b", "=+&%xx%20");
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").queryParam("a", "{b}").resolveTemplatesFromEncoded(m).build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c?a=%3D%2B%26%25xx%20"), uri);
+    }
+
+    @Test
+    public void testBuildFragmentTemplates() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").fragment("{foo}")
+                .build("x", "y", "z");
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x#x"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").fragment("{foo}")
+                .buildFromMap(m);
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x#x"), uri);
+    }
+
+    @Test
+    public void testResolveTemplateFromFragmentTemplates() {
+        URI uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").fragment("{foo}")
+                .resolveTemplate("foo", "x").resolveTemplate("bar", "y")
+                .resolveTemplate("baz", "z").build();
+
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x#x"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        uri = UriBuilder.fromUri("http://localhost:8080/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").fragment("{foo}")
+                .resolveTemplates(m).build();
+        Assert.assertEquals(URI.create("http://localhost:8080/a/b/c/x/y/z/x#x"), uri);
+    }
+
+    @Test
+    public void testTemplatesDefaultPort() {
+        URI uri = UriBuilder.fromUri("http://localhost/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").build("x", "y", "z");
+        Assert.assertEquals(URI.create("http://localhost/a/b/c/x/y/z/x"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        uri = UriBuilder.fromUri("http://localhost/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").buildFromMap(m);
+        Assert.assertEquals(URI.create("http://localhost/a/b/c/x/y/z/x"), uri);
+    }
+
+    @Test
+    public void testResolveTemplatesDefaultPort() {
+        URI uri = UriBuilder.fromUri("http://localhost/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").resolveTemplate("foo", "x")
+                .resolveTemplate("bar", "y").resolveTemplate("baz" + "", "z").build();
+        Assert.assertEquals(URI.create("http://localhost/a/b/c/x/y/z/x"), uri);
+
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("foo", "x");
+        m.put("bar", "y");
+        m.put("baz", "z");
+        uri = UriBuilder.fromUri("http://localhost/a/b/c").path("/{foo}/{bar}/{baz}/{foo}").resolveTemplates(m).build();
+        Assert.assertEquals(URI.create("http://localhost/a/b/c/x/y/z/x"), uri);
+    }
+
+    @Test
+    public void testClone() {
+        final UriBuilder ub = UriBuilder.fromUri("http://user@localhost:8080/?query#fragment").path("a");
+        final URI full = ub.clone().path("b").build();
+        final URI base = ub.build();
+
+        Assert.assertEquals(URI.create("http://user@localhost:8080/a?query#fragment"), base);
+        Assert.assertEquals(URI.create("http://user@localhost:8080/a/b?query#fragment"), full);
+    }
+
+    @Test
+    public void testIllegalArgumentException() {
+        boolean caught = false;
+        try {
+            UriBuilder.fromPath(null);
+        } catch (final IllegalArgumentException e) {
+            caught = true;
+        }
+        Assert.assertTrue(caught);
+
+        caught = false;
+        try {
+            UriBuilder.fromUri((URI) null);
+        } catch (final IllegalArgumentException e) {
+            caught = true;
+        }
+        Assert.assertTrue(caught);
+
+        caught = false;
+        try {
+            UriBuilder.fromUri((String) null);
+        } catch (final IllegalArgumentException e) {
+            caught = true;
+        }
+        Assert.assertTrue(caught);
+    }
+
+    @Test
+    public void testUriEncoding() {
+        final URI expected = URI.create("http://localhost:8080/%5E");
+        assertEquals(expected, new JerseyUriBuilder().uri("http://localhost:8080/^").build());
+        assertEquals(expected, UriBuilder.fromUri("http://localhost:8080/^").build());
+    }
+
+    // Regression test for JERSEY-1324 fix.
+    @Test
+    public void testInvalidUriTemplateEncodedAsPath() {
+        assertEquals(URI.create("http%20ftp%20xml//:888:888/1:8080:80"),
+                new JerseyUriBuilder().uri("http ftp xml//:888:888/1:8080:80").build());
+    }
+
+    @Test
+    public void testVariableWithoutValue() {
+        boolean caught = false;
+        try {
+            UriBuilder.fromPath("http://localhost:8080").path("/{a}/{b}").buildFromEncoded("aVal");
+
+        } catch (final IllegalArgumentException e) {
+            caught = true;
+        }
+        Assert.assertTrue(caught);
+    }
+
+    @Test
+    public void testPortValue() {
+        boolean caught = false;
+        try {
+            UriBuilder.fromPath("http://localhost").port(-2);
+        } catch (final IllegalArgumentException e) {
+            caught = true;
+        }
+        Assert.assertTrue(caught);
+    }
+
+    @Test
+    public void testPortSetting() throws URISyntaxException {
+        URI uri;
+
+        uri = new JerseyUriBuilder().uri("http://localhost").port(8080).build();
+        Assert.assertEquals(URI.create("http://localhost:8080"), uri);
+
+        uri = new JerseyUriBuilder().uri(new URI("http://localhost")).port(8080).build();
+        Assert.assertEquals(URI.create("http://localhost:8080"), uri);
+
+        uri = new JerseyUriBuilder().uri("http://localhost/").port(8080).build();
+        Assert.assertEquals(URI.create("http://localhost:8080/"), uri);
+
+        uri = new JerseyUriBuilder().uri(new URI("http://localhost/")).port(8080).build();
+        Assert.assertEquals(URI.create("http://localhost:8080/"), uri);
+    }
+
+    @Test
+    public void testHostValue() {
+        boolean caught = false;
+        try {
+            UriBuilder.fromPath("http://localhost").host("");
+        } catch (final IllegalArgumentException e) {
+            caught = true;
+        }
+        Assert.assertTrue(caught);
+
+        URI uri = UriBuilder.fromPath("").host("abc").build();
+        Assert.assertEquals(URI.create("//abc"), uri);
+
+        uri = UriBuilder.fromPath("").host("abc").host(null).build();
+        Assert.assertEquals(URI.create(""), uri);
+    }
+
+    /**
+     * This test has been rewritten as part of fix for JERSEY-2378.
+     * The new purpose of this test is to demonstrate how old behavior of UriBuilder.build() method
+     * wrt. unresolved templates can be achieved via {@link org.glassfish.jersey.uri.UriComponent#encodeTemplateNames(String)}
+     * method.
+     */
+    @Test
+    public void testEncodeTemplateNames() {
+        final URI uri = URI.create(UriComponent.encodeTemplateNames(UriBuilder.fromPath("http://localhost:8080").path(
+                "/{a}/{b}").replaceQuery("q={c}").toTemplate()));
+        Assert.assertEquals(URI.create("http://localhost:8080/%7Ba%7D/%7Bb%7D?q=%7Bc%7D"), uri);
+    }
+
+    @Test
+    public void resolveTemplateTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}")
+                .queryParam("query", "{q}");
+        uriBuilder.resolveTemplate("a", "param-a");
+        uriBuilder.resolveTemplate("q", "param-q");
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("a", "ignored-a");
+        m.put("b", "param-b");
+        m.put("q", "ignored-q");
+        Assert.assertEquals(URI.create("http://localhost:8080/param-a/param-b?query=param-q"), uriBuilder.buildFromMap(m));
+    }
+
+    @Test
+    public void resolveTemplateFromEncodedTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}").path("{c}")
+                .queryParam("query", "{q}");
+        uriBuilder.resolveTemplateFromEncoded("a", "x/y/z%3F%20");
+        uriBuilder.resolveTemplateFromEncoded("q", "q?%20%26");
+        uriBuilder.resolveTemplate("c", "paramc1/paramc2");
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("a", "ignored-a");
+        m.put("b", "param-b/aaa");
+        m.put("q", "ignored-q");
+        Assert.assertEquals("http://localhost:8080/x/y/z%3F%20/param-b/aaa/paramc1%2Fparamc2?query=q%3F%20%26",
+                uriBuilder.buildFromEncodedMap(m).toString());
+    }
+
+    @Test
+    public void resolveTemplateWithoutEncodedTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}").path("{c}")
+                .queryParam("query", "{q}");
+        uriBuilder.resolveTemplate("a", "x/y/z%3F%20");
+        uriBuilder.resolveTemplate("q", "q?%20%26");
+        uriBuilder.resolveTemplate("c", "paramc1/paramc2");
+        final Map<String, Object> m = new HashMap<String, Object>();
+        m.put("a", "ignored-a");
+        m.put("b", "param-b/aaa");
+        m.put("q", "ignored-q");
+        Assert.assertEquals("http://localhost:8080/x%2Fy%2Fz%253F%2520/param-b%2Faaa/paramc1%2Fparamc2?query=q%3F%2520%2526",
+                uriBuilder.buildFromMap(m).toString());
+    }
+
+    @Test
+    public void resolveTemplateWithEncodedSlashTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}")
+                .queryParam("query", "{q}");
+        uriBuilder.resolveTemplate("a", "param-a/withSlash", false);
+        uriBuilder.resolveTemplate("b", "param-b/withEncodedSlash", true);
+        uriBuilder.resolveTemplate("q", "param-q", true);
+        Assert.assertEquals(URI.create("http://localhost:8080/param-a/withSlash/param-b%2FwithEncodedSlash?query=param-q"),
+                uriBuilder.build());
+        uriBuilder.build();
+    }
+
+    @Test
+    public void resolveTemplatesTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}")
+                .queryParam("query", "{q}");
+
+        uriBuilder.resolveTemplate("a", "param-a");
+        uriBuilder.resolveTemplate("q", "param-q");
+        final Map<String, Object> buildMap = new HashMap<String, Object>();
+        buildMap.put("a", "ignored-a");
+        buildMap.put("b", "param-b");
+        buildMap.put("q", "ignored-q");
+        Assert.assertEquals(URI.create("http://localhost:8080/param-a/param-b?query=param-q"), uriBuilder.buildFromMap(buildMap));
+    }
+
+    @Test
+    public void resolveTemplatesFromEncodedTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}").path("{c}")
+                .queryParam("query", "{q}");
+
+        final Map<String, Object> resolveMap = new HashMap<String, Object>();
+        resolveMap.put("a", "x/y/z%3F%20");
+        resolveMap.put("q", "q?%20%26");
+        resolveMap.put("c", "paramc1/paramc2");
+        uriBuilder.resolveTemplatesFromEncoded(resolveMap);
+        final Map<String, Object> buildMap = new HashMap<String, Object>();
+        buildMap.put("b", "param-b/aaa");
+        Assert.assertEquals("http://localhost:8080/x/y/z%3F%20/param-b/aaa/paramc1/paramc2?query=q%3F%20%26",
+                uriBuilder.buildFromEncodedMap(buildMap).toString());
+    }
+
+    @Test
+    public void resolveTemplatesFromNotEncodedTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}").path("{c}")
+                .queryParam("query", "{q}");
+
+        final Map<String, Object> resolveMap = new HashMap<String, Object>();
+        resolveMap.put("a", "x/y/z%3F%20");
+        resolveMap.put("q", "q?%20%26");
+        resolveMap.put("c", "paramc1/paramc2");
+        uriBuilder.resolveTemplates(resolveMap);
+        final Map<String, Object> buildMap = new HashMap<String, Object>();
+        buildMap.put("b", "param-b/aaa");
+        Assert.assertEquals("http://localhost:8080/x%2Fy%2Fz%253F%2520/param-b%2Faaa/paramc1%2Fparamc2?query=q%3F%2520%2526",
+                uriBuilder.buildFromMap(buildMap).toString());
+    }
+
+    @Test
+    public void resolveTemplatesEncodeSlash() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}").path("{c}")
+                .queryParam("query", "{q}");
+
+        final Map<String, Object> resolveMap = new HashMap<String, Object>();
+        resolveMap.put("a", "x/y/z%3F%20");
+        resolveMap.put("q", "q?%20%26");
+        resolveMap.put("c", "paramc1/paramc2");
+        uriBuilder.resolveTemplates(resolveMap, false);
+        final Map<String, Object> buildMap = new HashMap<String, Object>();
+        buildMap.put("b", "param-b/aaa");
+        Assert.assertEquals("http://localhost:8080/x/y/z%253F%2520/param-b/aaa/paramc1/paramc2?query=q%3F%2520%2526",
+                uriBuilder.buildFromMap(buildMap, false).toString());
+    }
+
+    @Test
+    public void resolveTemplatesWithEncodedSlashTest() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{a}").path("{b}")
+                .queryParam("query", "{q}");
+        final Map<String, Object> resolveMap = new HashMap<String, Object>();
+        resolveMap.put("a", "param-a/withSlash");
+        resolveMap.put("q", "param-q");
+        uriBuilder.resolveTemplates(resolveMap, false);
+        uriBuilder.resolveTemplate("b", "param-b/withEncodedSlash", true);
+        Assert.assertEquals(URI.create("http://localhost:8080/param-a/withSlash/param-b%2FwithEncodedSlash?query=param-q"),
+                uriBuilder.build());
+        uriBuilder.build();
+    }
+
+    @Test
+    public void resolveTemplateMultipleCall() {
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8080").path("{start}").path("{a}")
+                .resolveTemplate("a", "first-a").path("{a}").resolveTemplate("a", "second-a")
+                .path("{a}/{a}").resolveTemplate("a", "twice-a");
+
+        Assert.assertEquals(URI.create("http://localhost:8080/start-path/first-a/second-a/twice-a/twice-a"),
+                uriBuilder.build("start-path"));
+    }
+
+    @Test
+    public void replaceWithEmtpySchemeFromUriTest() throws URISyntaxException {
+        final String uriOrig = "ftp://ftp.is.co.za/rfc/rfc1808.txt";
+        final URI uriReplace = new URI(null, "ftp.is.co.za", "/test/rfc1808.txt", null, null);
+        final URI uri = UriBuilder.fromUri(new URI(uriOrig)).uri(uriReplace).build();
+        Assert.assertEquals("ftp://ftp.is.co.za/test/rfc1808.txt", uri.toString());
+    }
+
+    @Test
+    public void replaceWithEmptySchemeFromStringTest() throws URISyntaxException {
+        final String uriOrig = "ftp://ftp.is.co.za/rfc/rfc1808.txt";
+        final URI uriReplace = new URI(null, "ftp.is.co.za", "/test/rfc1808.txt", null, null);
+
+        final URI uri = UriBuilder.fromUri(new URI(uriOrig)).uri(uriReplace.toASCIIString()).build();
+        Assert.assertEquals("ftp://ftp.is.co.za/test/rfc1808.txt", uri.toString());
+    }
+
+    @Test
+    public void replaceWithEmptyQueryFromStringTest() throws URISyntaxException {
+        final String uriOrig = "ftp://ftp.is.co.za/rfc/rfc1808.txt?a=1";
+        final URI uriReplace = new URI(null, "ftp.is.co.za", "/test/rfc1808.txt", null, null);
+
+        final URI uri = UriBuilder.fromUri(new URI(uriOrig)).uri(uriReplace.toASCIIString()).build();
+        Assert.assertEquals("ftp://ftp.is.co.za/test/rfc1808.txt?a=1", uri.toString());
+    }
+
+    @Test
+    public void replaceWithEmptyFragmentFromStringTest() throws URISyntaxException {
+        final String uriOrig = "ftp://ftp.is.co.za/rfc/rfc1808.txt#myFragment";
+        final URI uriReplace = new URI(null, "ftp.is.co.za", "/test/rfc1808.txt", null, null);
+
+        final URI uri = UriBuilder.fromUri(new URI(uriOrig)).uri(uriReplace.toASCIIString()).build();
+        Assert.assertEquals("ftp://ftp.is.co.za/test/rfc1808.txt#myFragment", uri.toString());
+    }
+
+    @Test
+    public void replaceOpaqueUriWithNonOpaqueFromStringTest() throws URISyntaxException {
+        final String first = "news:comp.lang.java";
+        final String second = "http://comp.lang.java";
+        UriBuilder.fromUri(new URI(first)).uri(second);
+    }
+
+    @Test
+    public void replaceOpaqueUriWithNonOpaqueFromStringTest2() throws URISyntaxException {
+        final String first = "news:comp.lang.java";
+        final String second = "http://comp.lang.java";
+        UriBuilder.fromUri(new URI(first)).scheme("http").uri(second);
+    }
+
+    @Test
+    public void replaceOpaqueUriWithNonOpaqueFromUriTest() throws URISyntaxException {
+        final String first = "news:comp.lang.java";
+        final String second = "http://comp.lang.java";
+        UriBuilder.fromUri(new URI(first)).uri(new URI(second));
+    }
+
+    @Test
+    public void testQueryParamEncoded() {
+        final UriBuilder uriBuilder = UriBuilder.fromUri("http://localhost:8080/path");
+        uriBuilder.queryParam("query", "%dummy23");
+        Assert.assertEquals("http://localhost:8080/path?query=%25dummy23", uriBuilder.build().toString());
+    }
+
+    @Test
+    public void testQueryParamEncoded2() {
+        final UriBuilder uriBuilder = UriBuilder.fromUri("http://localhost:8080/path");
+        uriBuilder.queryParam("query", "{param}");
+        Assert.assertEquals("http://localhost:8080/path?query=%25dummy23", uriBuilder.build("%dummy23").toString());
+    }
+
+    @Test
+    public void testQueryParamEncoded3() {
+        final UriBuilder uriBuilder = UriBuilder.fromUri("http://localhost:8080/path");
+        uriBuilder.queryParam("query", "{param}");
+        Assert.assertEquals("http://localhost:8080/path?query=%2525test", uriBuilder.build("%25test").toString());
+    }
+
+    @Test
+    public void testQueryParamEncoded4() {
+        final UriBuilder uriBuilder = UriBuilder.fromUri("http://localhost:8080/path");
+        uriBuilder.queryParam("query", "{param}");
+        Assert.assertEquals("http://localhost:8080/path?query=%25test", uriBuilder.buildFromEncoded("%25test").toString());
+    }
+
+    @Test
+    public void testQueryParamEncoded5() {
+        final UriBuilder uriBuilder = UriBuilder.fromUri("http://localhost:8080/path");
+        uriBuilder.queryParam("query", "! # $ & ' ( ) * + , / : ; = ? @ [ ]");
+        Assert.assertEquals(
+                "http://localhost:8080/path?query=%21+%23+%24+%26+%27+%28+%29+%2A+%2B+%2C+%2F+%3A+%3B+%3D+%3F+%40+%5B+%5D",
+                uriBuilder.build().toString());
+    }
+
+}
diff --git a/tests/e2e-core-common/src/test/resources/META-INF/services/org.glassfish.jersey.tests.e2e.common.config.TestContract b/tests/e2e-core-common/src/test/resources/META-INF/services/org.glassfish.jersey.tests.e2e.common.config.TestContract
new file mode 100644
index 0000000..9c92e56
--- /dev/null
+++ b/tests/e2e-core-common/src/test/resources/META-INF/services/org.glassfish.jersey.tests.e2e.common.config.TestContract
@@ -0,0 +1,2 @@
+org.glassfish.jersey.tests.e2e.common.config.TestServiceA
+org.glassfish.jersey.tests.e2e.common.config.TestServiceC
\ No newline at end of file
diff --git a/tests/e2e-core-common/src/test/resources/surefire.policy b/tests/e2e-core-common/src/test/resources/surefire.policy
new file mode 100644
index 0000000..4e99a5d
--- /dev/null
+++ b/tests/e2e-core-common/src/test/resources/surefire.policy
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017, 2018 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
+ */
+
+// we do not care about java lib itself
+grant codebase "file:${java.home}/-" {
+  permission java.security.AllPermission;
+};
+
+// we do not care about our dependencies
+grant codebase "file:${settings.localRepository}/-" {
+  permission java.security.AllPermission;
+};
+
+// this is to be able to set runtime delegate instance in jax-rs from the tests
+// and to run multi-threaded tests
+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.lang.RuntimePermission "getClassLoader";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
+  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+};
+
+grant codebase "file:${project.build.directory}/classes/-" {
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+  permission java.lang.RuntimePermission "accessDeclaredMembers";
+  permission java.lang.RuntimePermission "getClassLoader";
+  permission java.lang.RuntimePermission "modifyThread";
+  permission java.util.PropertyPermission "*", "read";
+  permission java.io.FilePermission "<<ALL FILES>>", "read";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
+  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+};
diff --git a/tests/e2e-entity/pom.xml b/tests/e2e-entity/pom.xml
new file mode 100644
index 0000000..46f38e2
--- /dev/null
+++ b/tests/e2e-entity/pom.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-entity</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e-entity</name>
+
+    <description>Jersey E2E Entity tests</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-freemarker</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-mustache</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-binding</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-signature</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth2-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>xmlunit</groupId>
+            <artifactId>xmlunit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>xdk</id>
+            <properties>
+                <!-- do not use security manager for xdk -->
+                <surefire.security.argline />
+            </properties>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+    </profiles>
+
+</project>
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractParameterTypeArgumentOrderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractParameterTypeArgumentOrderTest.java
new file mode 100644
index 0000000..a81756f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractParameterTypeArgumentOrderTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+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.glassfish.jersey.test.JerseyTest;
+
+/**
+ * Parent for set of ParameterTypeArgumentOrder tests.
+ *
+ * Contains all the providers and resources for the tests. The resource config creation and test methods were separated into
+ * subclasses.
+ *
+ * @author Paul Sandoz
+ */
+public abstract class AbstractParameterTypeArgumentOrderTest extends JerseyTest {
+
+    @Provider
+    public static class ObjectWriter implements MessageBodyWriter {
+
+        @Override
+        public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(final Object o, final Class type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final Object o, final Class type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            entityStream.write(o.toString().getBytes());
+        }
+    }
+
+    public static class GenericClassWriter<T> implements MessageBodyWriter<T> {
+
+        private final Class c;
+
+        GenericClassWriter(final Class c) {
+            this.c = c;
+        }
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return c.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(final T t, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @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 {
+            entityStream.write((c.getSimpleName() + type.getSimpleName()).getBytes());
+        }
+    }
+
+    public static class A {}
+
+    @Provider
+    public static class AWriter extends GenericClassWriter<A> {
+
+        public AWriter() {
+            super(A.class);
+        }
+    }
+
+    public static class B extends A {}
+
+    @Provider
+    public static class BWriter extends GenericClassWriter<B> {
+
+        public BWriter() {
+            super(B.class);
+        }
+    }
+
+    public static class C extends B {}
+
+    @Provider
+    public static class CWriter extends GenericClassWriter<C> {
+
+        public CWriter() {
+            super(C.class);
+        }
+    }
+
+    @Path("/")
+    public static class ClassResource {
+
+        @GET
+        @Path("a")
+        public A getA() {
+            return new A();
+        }
+
+        @GET
+        @Path("b")
+        public B getB() {
+            return new B();
+        }
+
+        @GET
+        @Path("c")
+        public C getC() {
+            return new C();
+        }
+    }
+
+    public static class GenericClassReaderWriter<T> implements MessageBodyWriter<T>, MessageBodyReader<T> {
+
+        private final Class c;
+
+        GenericClassReaderWriter(final Class c) {
+            this.c = c;
+        }
+
+        @Override
+        public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                  final MediaType mediaType) {
+            return c.isAssignableFrom(type);
+        }
+
+        @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 (T) c.newInstance();
+            } catch (final Exception ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return c.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(final T t, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @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 {
+            entityStream.write((c.getSimpleName() + type.getSimpleName()).getBytes());
+        }
+    }
+
+    @Provider
+    public static class AReaderWriter<T> extends GenericClassReaderWriter<T> {
+
+        public AReaderWriter() {
+            super(A.class);
+        }
+    }
+
+    @Provider
+    public static class BReaderWriter extends GenericClassReaderWriter<B> {
+
+        public BReaderWriter() {
+            super(B.class);
+        }
+    }
+
+    @Provider
+    public static class CReaderWriter extends GenericClassReaderWriter<C> {
+
+        public CReaderWriter() {
+            super(C.class);
+        }
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractTypeTester.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractTypeTester.java
new file mode 100644
index 0000000..8f3c8a2
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractTypeTester.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+
+import javax.ws.rs.ConstrainedTo;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Abstract entity type tester base class.
+ *
+ * @author Paul Sandoz
+ * @author Martin Matula
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public abstract class AbstractTypeTester extends JerseyTest {
+
+    protected static final ThreadLocal localRequestEntity = new ThreadLocal();
+
+    public abstract static class AResource<T> {
+
+        @POST
+        public T post(T t) {
+            return t;
+        }
+    }
+
+    public static class RequestEntityInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {
+            OutputStream original = writerInterceptorContext.getOutputStream();
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            writerInterceptorContext.setOutputStream(baos);
+            writerInterceptorContext.proceed();
+            final byte[] requestEntity = baos.toByteArray();
+            writerInterceptorContext.setProperty("requestEntity", requestEntity);
+            original.write(requestEntity);
+        }
+    }
+
+    public static class ResponseFilter implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+            localRequestEntity.set(requestContext.getProperty("requestEntity"));
+        }
+    }
+
+    /**
+     * Looks for all resources and providers declared as inner classes of the subclass of this class
+     * and adds them to the returned ResourceConfig (unless constrained to client side).
+     *
+     * @return ResourceConfig instance
+     */
+    @Override
+    protected Application configure() {
+        HashSet<Class<?>> classes = new HashSet<Class<?>>();
+
+        for (Class<?> cls : getClass().getDeclaredClasses()) {
+            if (cls.getAnnotation(Path.class) != null) {
+                classes.add(cls);
+            } else if (cls.getAnnotation(Provider.class) != null) {
+                final ConstrainedTo constrainedTo = cls.getAnnotation(ConstrainedTo.class);
+                if (constrainedTo == null || constrainedTo.value() == RuntimeType.SERVER) {
+                    classes.add(cls);
+                }
+            }
+        }
+
+        return new ResourceConfig(classes);
+    }
+
+    /**
+     * Looks for all providers declared as inner classes of the subclass of this class
+     * and adds them to the client configuration (unless constrained to server side).
+     */
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(RequestEntityInterceptor.class);
+        config.register(ResponseFilter.class);
+
+        for (Class<?> cls : getClass().getDeclaredClasses()) {
+            if (cls.getAnnotation(Provider.class) != null) {
+                final ConstrainedTo constrainedTo = cls.getAnnotation(ConstrainedTo.class);
+                if (constrainedTo == null || constrainedTo.value() == RuntimeType.CLIENT) {
+                    config.register(cls);
+                }
+            }
+        }
+    }
+
+    protected <T> void _test(T in, Class resource) {
+        _test(in, resource, true);
+    }
+
+    protected <T> void _test(T in, Class resource, MediaType m) {
+        _test(in, resource, m, true);
+    }
+
+    protected <T> void _test(T in, Class resource, boolean verify) {
+        _test(in, resource, MediaType.TEXT_PLAIN_TYPE, verify);
+    }
+
+    protected <T> void _test(T in, Class resource, MediaType m, boolean verify) {
+        WebTarget target = target(resource.getSimpleName());
+        Response response = target.request().post(Entity.entity(in, m));
+
+        byte[] inBytes = getRequestEntity();
+        byte[] outBytes = getEntityAsByteArray(response);
+
+        if (verify) {
+            _verify(inBytes, outBytes);
+        }
+    }
+
+    protected static byte[] getRequestEntity() {
+        try {
+            return (byte[]) localRequestEntity.get();
+        } finally {
+            localRequestEntity.set(null);
+        }
+    }
+
+    protected static void _verify(byte[] in, byte[] out) {
+        assertEquals(in.length, out.length);
+        for (int i = 0; i < in.length; i++) {
+            if (in[i] != out[i]) {
+                assertEquals("Index: " + i, in[i], out[i]);
+            }
+        }
+    }
+
+    protected static byte[] getEntityAsByteArray(Response r) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            ReaderWriter.writeTo(r.readEntity(InputStream.class), baos);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return baos.toByteArray();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/BeanStreamingTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/BeanStreamingTest.java
new file mode 100644
index 0000000..8882e6e
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/BeanStreamingTest.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class BeanStreamingTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(getClass().getDeclaredClasses());
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        for (Class<?> c : getClass().getDeclaredClasses()) {
+            if (c.getAnnotation(Provider.class) != null) {
+                config.register(c);
+            }
+        }
+    }
+
+    @Test
+    public void testBean() throws Exception {
+        Bean b = new Bean("bean", 123, 3.1415f);
+
+        // the following should work using BeanProvider which
+        // supports Bean.class for type application/bean
+        WebTarget r = target().path("/bean");
+        r.request().post(Entity.entity(b, "application/bean"), Bean.class);
+
+        try {
+            r = target().path("/plain");
+            r.request().post(Entity.entity(b, "text/plain"), Bean.class);
+            assertFalse(false);
+        } catch (ProcessingException ex) {
+            assertTrue(true);
+        }
+    }
+
+    @Test
+    public void testBeanWild() throws Exception {
+        Bean b = new Bean("bean", 123, 3.1415f);
+
+        // the following should work using BeanWildProvider which
+        // supports Bean.class for type application/*
+        target().path("/wild").request().post(Entity.entity(b, "application/wild-bean"), Bean.class);
+    }
+
+    @Test
+    public void testBean2() throws Exception {
+        Bean2 b = new Bean2("bean", 123, 3.1415f);
+
+        target().path("/bean2").request().post(Entity.entity(b, "application/bean"), Bean2.class);
+
+        try {
+            target().path("/plain2").request().post(Entity.entity(b, "text/plain"), Bean2.class);
+            assertFalse(false);
+        } catch (ProcessingException ex) {
+            assertTrue(true);
+        }
+    }
+
+    @Test
+    public void testBean2UsingBean() throws Exception {
+        Bean2 b = new Bean2("bean", 123, 3.1415f);
+
+        // the following should work using BeanProvider which
+        // supports Bean.class for type application/bean
+        target().path("/bean").request().post(Entity.entity(b, "application/bean"), Bean2.class);
+
+        try {
+            target().path("/plain").request().post(Entity.entity(b, "text/plain"), Bean2.class);
+            fail();
+        } catch (ProcessingException ex) {
+            // good
+        }
+    }
+
+    @Test
+    public void testBean2Wild() throws Exception {
+        Bean2 b = new Bean2("bean", 123, 3.1415f);
+
+        // the following should work using BeanWildProvider which
+        // supports Bean.class for type application/*
+        target().path("/wild2").request().post(Entity.entity(b, "application/wild-bean"), Bean2.class);
+    }
+
+    @Test
+    public void testBean2WildUsingBean() throws Exception {
+        Bean2 b = new Bean2("bean", 123, 3.1415f);
+
+        // the following should work using BeanWildProvider which
+        // supports Bean.class for type application/*
+        target().path("/wild").request().post(Entity.entity(b, "application/wild-bean"), Bean2.class);
+    }
+
+    public static class Bean implements Serializable {
+        private String string;
+        private int integer;
+        private float real;
+
+        public Bean() {
+        }
+
+        public Bean(String string, int integer, float real) {
+            this.string = string;
+            this.integer = integer;
+            this.real = real;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+
+        public int getInteger() {
+            return integer;
+        }
+
+        public void setInteger(int integer) {
+            this.integer = integer;
+        }
+
+        public float getReal() {
+            return real;
+        }
+
+        public void setReal(float real) {
+            this.real = real;
+        }
+    }
+
+    @Provider
+    @Produces("application/bean")
+    @Consumes("application/bean")
+    public static class BeanProvider extends AbstractMessageReaderWriterProvider<Bean> {
+
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean.class;
+        }
+
+        public Bean readFrom(
+                Class<Bean> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException {
+            ObjectInputStream oin = new ObjectInputStream(entityStream);
+            try {
+                return (Bean) oin.readObject();
+            } catch (ClassNotFoundException cause) {
+                IOException effect = new IOException(cause.getLocalizedMessage());
+                effect.initCause(cause);
+                throw effect;
+            }
+        }
+
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean.class;
+        }
+
+        public void writeTo(
+                Bean t,
+                Class<?> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException {
+            ObjectOutputStream out = new ObjectOutputStream(entityStream);
+            out.writeObject(t);
+            out.flush();
+        }
+    }
+
+    @Provider
+    @Produces("application/*")
+    @Consumes("application/*")
+    public static class BeanWildProvider extends BeanProvider {
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean.class;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean.class;
+        }
+    }
+
+    @Provider
+    @Produces("application/bean")
+    @Consumes("application/bean")
+    public static class Bean2Provider extends AbstractMessageReaderWriterProvider<Bean2> {
+
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean2.class;
+        }
+
+        public Bean2 readFrom(
+                Class<Bean2> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException {
+            ObjectInputStream oin = new ObjectInputStream(entityStream);
+            try {
+                return (Bean2) oin.readObject();
+            } catch (ClassNotFoundException cause) {
+                IOException effect = new IOException(cause.getLocalizedMessage());
+                effect.initCause(cause);
+                throw effect;
+            }
+        }
+
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean2.class;
+        }
+
+        public void writeTo(
+                Bean2 t,
+                Class<?> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException {
+            ObjectOutputStream out = new ObjectOutputStream(entityStream);
+            out.writeObject(t);
+            out.flush();
+        }
+    }
+
+    @Provider
+    @Produces("application/*")
+    @Consumes("application/*")
+    public static class Bean2WildProvider extends Bean2Provider {
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean2.class;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mt) {
+            return type == Bean2.class;
+        }
+    }
+
+    public static class Bean2 extends Bean {
+        public Bean2(String string, int integer, float real) {
+            super(string, integer, real);
+        }
+    }
+
+    @Path("/bean")
+    public static class BeanResource {
+        @POST
+        @Consumes("application/bean")
+        @Produces("application/bean")
+        public Bean post(Bean t) {
+            return t;
+        }
+    }
+
+    @Path("/bean2")
+    public static class Bean2Resource {
+        @POST
+        @Consumes("application/bean")
+        @Produces("application/bean")
+        public Bean2 post(Bean2 t) {
+            return t;
+        }
+    }
+
+    @Path("/plain")
+    public static class BeanTextPlainResource {
+        @POST
+        @Consumes("text/plain")
+        @Produces("text/plain")
+        public Bean post(Bean t) {
+            return t;
+        }
+    }
+
+    @Path("/plain2")
+    public static class Bean2TextPlainResource {
+        @POST
+        @Consumes("text/plain")
+        @Produces("text/plain")
+        public Bean2 post(Bean2 t) {
+            return t;
+        }
+    }
+
+    @Path("/wild")
+    public static class BeanWildResource {
+        @POST
+        @Consumes("application/*")
+        @Produces("application/*")
+        public Bean post(Bean t) {
+            return t;
+        }
+    }
+
+    @Path("/wild2")
+    public static class Bean2WildResource {
+        @POST
+        @Consumes("application/*")
+        @Produces("application/*")
+        public Bean2 post(Bean2 t) {
+            return t;
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/CharsetTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/CharsetTest.java
new file mode 100644
index 0000000..f81aa04
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/CharsetTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.xml.bind.JAXBContext;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.jettison.JettisonJaxbContext;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+import org.glassfish.jersey.client.ClientResponse;
+import org.junit.Test;
+
+/**
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class CharsetTest extends AbstractTypeTester {
+
+    private static String[] CHARSETS = {
+            "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16"
+    };
+
+    private static final String CONTENT = "\u00A9 CONTENT \u00FF \u2200 \u22FF";
+
+    @Path("/StringCharsetResource")
+    public static class StringCharsetResource {
+
+        @Path("US-ASCII")
+        @POST
+        @Produces("text/plain;charset=US-ASCII")
+        public String postUs_Ascii(String t) {
+            return t;
+        }
+
+        @Path("ISO-8859-1")
+        @POST
+        @Produces("text/plain;charset=ISO-8859-1")
+        public String postIso_8859_1(String t) {
+            return t;
+        }
+
+        @Path("UTF-8")
+        @POST
+        @Produces("text/plain;charset=UTF-8")
+        public String postUtf_8(String t) {
+            return t;
+        }
+
+        @Path("UTF-16BE")
+        @POST
+        @Produces("text/plain;charset=UTF-16BE")
+        public String postUtf_16be(String t) {
+            return t;
+        }
+
+        @Path("UTF-16LE")
+        @POST
+        @Produces("text/plain;charset=UTF-16LE")
+        public String postUtf_16le(String t) {
+            return t;
+        }
+
+        @Path("UTF-16")
+        @POST
+        @Produces("text/plain;charset=UTF-16")
+        public String postUtf_16(String t) {
+            return t;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return ((ResourceConfig) super.configure()).register(new JettisonFeature());
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        super.configureClient(config);
+        config.register(new JettisonFeature());
+        config.register(MyJaxbContextResolver.class);
+    }
+
+    @Test
+    public void testStringCharsetResource() {
+        String in = "\u00A9 CONTENT \u00FF \u2200 \u22FF";
+
+        WebTarget t = target().path("StringCharsetResource");
+
+        for (String charset : CHARSETS) {
+            Response r = t.path(charset).request().post(Entity.entity(in, "text/plain;charset=" + charset));
+
+            byte[] inBytes = getRequestEntity();
+            byte[] outBytes = getEntityAsByteArray(r);
+
+            _verify(inBytes, outBytes);
+        }
+    }
+
+    public abstract static class CharsetResource<T> {
+
+        @Context
+        HttpHeaders h;
+
+        @POST
+        @Produces("application/*")
+        public Response post(T t) {
+            return Response.ok(t, h.getMediaType()).build();
+        }
+    }
+
+    @Path("/StringResource")
+    public static class StringResource extends CharsetResource<String> {
+
+    }
+
+    @Test
+    public void testStringRepresentation() {
+        _test(CONTENT, StringResource.class);
+    }
+
+    @Path("/FormMultivaluedMapResource")
+    public static class FormMultivaluedMapResource extends CharsetResource<MultivaluedMap<String, String>> {
+
+    }
+
+    @Test
+    public void testFormMultivaluedMapRepresentation() {
+        MultivaluedMap<String, String> map = new MultivaluedHashMap<>();
+
+        map.add("name", "\u00A9 CONTENT \u00FF \u2200 \u22FF");
+        map.add("name", "� � �");
+        _test(map, FormMultivaluedMapResource.class, MediaType.APPLICATION_FORM_URLENCODED_TYPE);
+    }
+
+    @Path("/FormResource")
+    public static class FormResource extends CharsetResource<Form> {
+
+    }
+
+    @Test
+    public void testRepresentation() {
+        Form form = new Form();
+
+        form.param("name", "\u00A9 CONTENT \u00FF \u2200 \u22FF");
+        form.param("name", "� � �");
+        _test(form, FormResource.class, MediaType.APPLICATION_FORM_URLENCODED_TYPE);
+    }
+
+    @Path("/JSONObjectResource")
+    public static class JSONObjectResource extends CharsetResource<JSONObject> {
+
+    }
+
+    @Test
+    public void testJSONObjectRepresentation() throws Exception {
+        JSONObject object = new JSONObject();
+        object.put("userid", 1234)
+                .put("username", CONTENT)
+                .put("email", "a@b")
+                .put("password", "****");
+
+        _test(object, JSONObjectResource.class, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Path("/JSONOArrayResource")
+    public static class JSONOArrayResource extends CharsetResource<JSONArray> {
+
+    }
+
+    @Test
+    public void testJSONArrayRepresentation() throws Exception {
+        JSONArray array = new JSONArray();
+        array.put(CONTENT).put("Two").put("Three").put(1).put(2.0);
+
+        _test(array, JSONOArrayResource.class, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Path("/JAXBBeanResource")
+    public static class JAXBBeanResource extends CharsetResource<JaxbBean> {
+
+    }
+
+    @Test
+    public void testJAXBBeanXMLRepresentation() {
+        _test(new JaxbBean(CONTENT), JAXBBeanResource.class, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Test
+    public void testJAXBBeanJSONRepresentation() {
+        _test(new JaxbBean(CONTENT), JAXBBeanResource.class, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Provider
+    public static class MyJaxbContextResolver implements ContextResolver<JAXBContext> {
+
+        JAXBContext context;
+
+        public MyJaxbContextResolver() throws Exception {
+            context = new JettisonJaxbContext(JaxbBean.class);
+        }
+
+        public JAXBContext getContext(Class<?> objectType) {
+            return (objectType == JaxbBean.class) ? context : null;
+        }
+    }
+
+    @Test
+    public void testJAXBBeanJSONRepresentationWithContextResolver() throws Exception {
+        JaxbBean in = new JaxbBean(CONTENT);
+        WebTarget t = target("/JAXBBeanResource");
+        for (String charset : CHARSETS) {
+            Response rib = t.request().post(Entity.entity(in, "application/json;charset=" + charset));
+            byte[] inBytes = getRequestEntity();
+            byte[] outBytes = getEntityAsByteArray(rib);
+            _verify(inBytes, outBytes);
+        }
+    }
+
+    @Path("/ReaderResource")
+    public static class ReaderResource extends CharsetResource<Reader> {
+
+    }
+
+    @Test
+    public void testReaderRepresentation() throws Exception {
+        WebTarget t = target("/ReaderResource");
+        for (String charset : CHARSETS) {
+            Response rib = t.request().post(Entity.entity(new StringReader(CONTENT), "text/plain;charset=" + charset));
+            byte[] inBytes = getRequestEntity();
+            byte[] outBytes = getEntityAsByteArray(rib);
+            _verify(inBytes, outBytes);
+        }
+    }
+
+    @Override
+    public <T> void _test(T in, Class resource) {
+        _test(in, resource, MediaType.TEXT_PLAIN_TYPE);
+    }
+
+    @Override
+    public <T> void _test(T in, Class resource, MediaType m) {
+        WebTarget t = target(resource.getSimpleName());
+        for (String charset : CHARSETS) {
+            Map<String, String> p = new HashMap<>();
+            p.put("charset", charset);
+            MediaType _m = new MediaType(m.getType(), m.getSubtype(), p);
+            Response rib = t.request().post(Entity.entity(in, _m));
+            byte[] inBytes = getRequestEntity();
+            byte[] outBytes = getEntityAsByteArray(rib);
+            _verify(inBytes, outBytes);
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ContextResolverMediaTypeTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ContextResolverMediaTypeTest.java
new file mode 100644
index 0000000..e9250e3
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ContextResolverMediaTypeTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+@RunWith(Enclosed.class)
+public class ContextResolverMediaTypeTest {
+
+    @Produces("text/plain")
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class TextPlainContextResolver implements ContextResolver<String> {
+
+        public String getContext(Class<?> objectType) {
+            return "text/plain";
+        }
+    }
+
+    @Produces("text/*")
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class TextContextResolver implements ContextResolver<String> {
+
+        public String getContext(Class<?> objectType) {
+            return "text/*";
+        }
+    }
+
+    @Produces("*/*")
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class WildcardContextResolver implements ContextResolver<String> {
+
+        public String getContext(Class<?> objectType) {
+            return "*/*";
+        }
+    }
+
+    @Produces({"text/plain", "text/html"})
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class TextPlainHtmlContextResolver implements ContextResolver<String> {
+
+        public String getContext(Class<?> objectType) {
+            return "text/plain/html";
+        }
+
+    }
+
+    @Produces("text/html")
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class TextHtmlContextResolver implements ContextResolver<String> {
+
+        public String getContext(Class<?> objectType) {
+            return "text/html";
+        }
+
+    }
+
+    @Path("/")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class ContextResource {
+
+        @Context
+        Providers p;
+
+        @Context
+        ContextResolver<String> cr;
+
+        @GET
+        @Path("{id: .+}")
+        public String get(@PathParam("id") MediaType m) {
+            ContextResolver<String> cr = p.getContextResolver(String.class, m);
+
+            // Verify cache is working
+            ContextResolver<String> cachedCr = p.getContextResolver(String.class, m);
+            assertEquals(cr, cachedCr);
+
+            if (cr == null) {
+                return "NULL";
+            } else {
+                return cr.getContext(null);
+            }
+        }
+    }
+
+    public static class ProduceTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(ContextResource.class,
+                    TextPlainContextResolver.class,
+                    TextContextResolver.class,
+                    WildcardContextResolver.class);
+        }
+
+        @Test
+        public void testProduce() throws IOException {
+
+            WebTarget target = target();
+
+            assertEquals("text/plain", target.path("text/plain").request().get(String.class));
+            assertEquals("text/*", target.path("text/*").request().get(String.class));
+            assertEquals("*/*", target.path("*/*").request().get(String.class));
+
+            assertEquals("text/*", target.path("text/html").request().get(String.class));
+
+            assertEquals("*/*", target.path("application/xml").request().get(String.class));
+            assertEquals("*/*", target.path("application/*").request().get(String.class));
+        }
+    }
+
+    public static class ProducesTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(ContextResource.class,
+                    TextPlainHtmlContextResolver.class,
+                    TextContextResolver.class,
+                    WildcardContextResolver.class);
+        }
+
+        @Test
+        public void testProduces() throws IOException {
+            WebTarget target = target();
+
+            assertEquals("text/plain/html", target.path("text/plain").request().get(String.class));
+            assertEquals("text/plain/html", target.path("text/html").request().get(String.class));
+            assertEquals("text/*", target.path("text/*").request().get(String.class));
+            assertEquals("*/*", target.path("*/*").request().get(String.class));
+
+            assertEquals("text/*", target.path("text/csv").request().get(String.class));
+
+            assertEquals("*/*", target.path("application/xml").request().get(String.class));
+            assertEquals("*/*", target.path("application/*").request().get(String.class));
+        }
+    }
+
+    public static class ProducesSeparateTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(ContextResource.class,
+                    TextPlainContextResolver.class,
+                    TextHtmlContextResolver.class,
+                    TextContextResolver.class,
+                    WildcardContextResolver.class);
+        }
+
+        @Test
+        public void testProducesSeparate() throws IOException {
+            WebTarget target = target();
+
+            assertEquals("text/plain", target.path("text/plain").request().get(String.class));
+            assertEquals("text/html", target.path("text/html").request().get(String.class));
+            assertEquals("text/*", target.path("text/*").request().get(String.class));
+            assertEquals("*/*", target.path("*/*").request().get(String.class));
+
+            assertEquals("text/*", target.path("text/csv").request().get(String.class));
+
+            assertEquals("*/*", target.path("application/xml").request().get(String.class));
+            assertEquals("*/*", target.path("application/*").request().get(String.class));
+        }
+    }
+
+    public static class ProducesXXXTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(ContextResource.class,
+                    TextPlainContextResolver.class,
+                    TextHtmlContextResolver.class);
+        }
+
+        @Test
+        public void testProducesXXX() throws IOException {
+            WebTarget target = target();
+
+            assertEquals("text/plain", target.path("text/plain").request().get(String.class));
+            assertEquals("text/html", target.path("text/html").request().get(String.class));
+            assertEquals("NULL", target.path("text/*").request().get(String.class));
+            assertEquals("NULL", target.path("*/*").request().get(String.class));
+
+            assertEquals("NULL", target.path("text/csv").request().get(String.class));
+
+            assertEquals("NULL", target.path("application/xml").request().get(String.class));
+            assertEquals("NULL", target.path("application/*").request().get(String.class));
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyEntityTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyEntityTest.java
new file mode 100644
index 0000000..3624e43
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyEntityTest.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.ResponseProcessingException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+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.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * JERSEY-1540.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class EmptyEntityTest extends AbstractTypeTester {
+
+    public static class TestEmtpyBean {
+        private final String value;
+
+        public TestEmtpyBean(String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+    @Provider
+    public static class TestEmptyBeanProvider implements MessageBodyReader<TestEmtpyBean>, MessageBodyWriter<TestEmtpyBean> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return TestEmtpyBean.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public TestEmtpyBean readFrom(Class<TestEmtpyBean> type,
+                                      Type genericType,
+                                      Annotation[] annotations,
+                                      MediaType mediaType,
+                                      MultivaluedMap<String, String> httpHeaders,
+                                      InputStream entityStream) throws IOException, WebApplicationException {
+
+            final String value = ReaderWriter.readFromAsString(entityStream, mediaType);
+            return new TestEmtpyBean(value);
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return TestEmtpyBean.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(TestEmtpyBean testEmtpyBean,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(TestEmtpyBean testEmtpyBean,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            ReaderWriter.writeToAsString(testEmtpyBean.getValue(), entityStream, mediaType);
+        }
+    }
+
+    @XmlRootElement
+    public static class TestJaxbBean {
+
+    }
+
+    @Path("empty")
+    public static class EmptyResource {
+
+        @POST
+        @Path("jaxbelem")
+        public String jaxbelem(JAXBElement<String> jaxb) {
+            return "ERROR"; // shouldn't be called
+        }
+
+        @POST
+        @Path("jaxbbean")
+        public String jaxbbean(TestJaxbBean bean) {
+            return "ERROR"; // shouldn't be called
+        }
+
+        @POST
+        @Path("jaxblist")
+        public String jaxblist(List<TestJaxbBean> beans) {
+            return "ERROR"; // shouldn't be called
+        }
+
+        @POST
+        @Path("boolean")
+        public String bool(Boolean b) {
+            return "ERROR"; // shouldn't be called
+        }
+
+        @POST
+        @Path("character")
+        public String character(Character c) {
+            return "ERROR"; // shouldn't be called
+        }
+
+        @POST
+        @Path("integer")
+        public String integer(Integer i) {
+            return "ERROR"; // shouldn't be called
+        }
+
+        @POST
+        @Path("emptybean")
+        public String emptyBean(TestEmtpyBean bean) {
+            return (bean.getValue().isEmpty()) ? "PASSED" : "ERROR";
+        }
+
+        @GET
+        @Path("getempty")
+        public Response getEmpty(@Context HttpHeaders headers) {
+            return Response.ok().type(headers.getAcceptableMediaTypes().get(0)).header(HttpHeaders.CONTENT_LENGTH, 0).build();
+        }
+    }
+
+    public EmptyEntityTest() {
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testSendEmptyJAXBElement() {
+        WebTarget target = target("empty/jaxbelem");
+        final Response response = target.request().post(Entity.entity(null, MediaType.APPLICATION_XML_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSendEmptyJAXBbean() {
+        WebTarget target = target("empty/jaxbbean");
+        final Response response = target.request().post(Entity.entity(null, MediaType.APPLICATION_XML_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSendEmptyJAXBlist() {
+        WebTarget target = target("empty/jaxblist");
+        final Response response = target.request().post(Entity.entity(null, MediaType.APPLICATION_XML_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSendEmptyBoolean() {
+        WebTarget target = target("empty/boolean");
+        final Response response = target.request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSendEmptyCharacter() {
+        WebTarget target = target("empty/character");
+        final Response response = target.request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSendEmptyInteger() {
+        WebTarget target = target("empty/integer");
+        final Response response = target.request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSendEmptyBean() {
+        WebTarget target = target("empty/emptybean");
+        final Response response = target.request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("PASSED", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testReceiveEmptyJAXBElement() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("application/xml").get();
+        assertEquals(200, response.getStatus());
+
+        try {
+            response.readEntity(new GenericType<JAXBElement<String>>() {
+            });
+            fail("ProcessingException expected.");
+        } catch (ProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+
+        try {
+            target.request("application/xml").get(new GenericType<JAXBElement<String>>() {
+            });
+            fail("ResponseProcessingException expected.");
+        } catch (ResponseProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void testReceiveEmptyJAXBbean() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("application/xml").get();
+        assertEquals(200, response.getStatus());
+
+        try {
+            response.readEntity(TestJaxbBean.class);
+            fail("ProcessingException expected.");
+        } catch (ProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+
+        try {
+            target.request("application/xml").get(TestJaxbBean.class);
+            fail("ResponseProcessingException expected.");
+        } catch (ResponseProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void testReceiveEmptyJAXBlist() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("application/xml").get();
+        assertEquals(200, response.getStatus());
+
+        try {
+            response.readEntity(new GenericType<List<TestJaxbBean>>() {
+            });
+            fail("ProcessingException expected.");
+        } catch (ProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+
+        try {
+            target.request("application/xml").get(new GenericType<List<TestJaxbBean>>() {
+            });
+            fail("ResponseProcessingException expected.");
+        } catch (ResponseProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void testReceiveEmptyBoolean() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("text/plain").get();
+        assertEquals(200, response.getStatus());
+
+        try {
+            response.readEntity(Boolean.class);
+            fail("ProcessingException expected.");
+        } catch (ProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+
+        try {
+            target.request("text/plain").get(Boolean.class);
+            fail("ResponseProcessingException expected.");
+        } catch (ResponseProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void testReceiveEmptyCharacter() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("text/plain").get();
+        assertEquals(200, response.getStatus());
+
+        try {
+            response.readEntity(Character.class);
+            fail("ProcessingException expected.");
+        } catch (ProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+
+        try {
+            target.request("text/plain").get(Character.class);
+            fail("ResponseProcessingException expected.");
+        } catch (ResponseProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void testReceiveEmptyInteger() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("text/plain").get();
+        assertEquals(200, response.getStatus());
+
+        try {
+            response.readEntity(Integer.class);
+            fail("ProcessingException expected.");
+        } catch (ProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+
+        try {
+            target.request("text/plain").get(Integer.class);
+            fail("ResponseProcessingException expected.");
+        } catch (ResponseProcessingException ex) {
+            assertSame(NoContentException.class, ex.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void testReceiveEmptyBean() {
+        WebTarget target = target("empty/getempty");
+        final Response response = target.request("text/plain").get();
+        assertEquals(200, response.getStatus());
+
+        assertTrue(response.readEntity(TestEmtpyBean.class).getValue().isEmpty());
+        assertTrue(target.request("text/plain").get(TestEmtpyBean.class).getValue().isEmpty());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestToEntityParamTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestToEntityParamTest.java
new file mode 100644
index 0000000..46258b6
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestToEntityParamTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import java.util.Map;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for JERSEY-1579.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class EmptyRequestToEntityParamTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(EntityResource.class);
+    }
+
+    @Path("/")
+    public static class EntityResource {
+
+        @DELETE
+        public void delete(Map<String, String> entity) {
+            Assert.assertNull(entity);
+        }
+    }
+
+    @Test
+    public void testEmptyRequest() {
+        Response response = target().request().delete(Response.class);
+        Assert.assertEquals(204, response.getStatus());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestWithJaxbTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestWithJaxbTest.java
new file mode 100644
index 0000000..a7c4f0d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestWithJaxbTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jettison.JettisonConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.jettison.JettisonJaxbContext;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+@RunWith(Enclosed.class)
+public class EmptyRequestWithJaxbTest {
+
+    @SuppressWarnings("UnusedParameters")
+    @Path("/")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class Resource {
+
+        @POST
+        public void bean(JaxbBean b) {
+        }
+
+        @Path("type")
+        @POST
+        public void type(JaxbBeanType b) {
+        }
+
+        @Path("list-bean")
+        @POST
+        public void listBean(List<JaxbBean> b) {
+        }
+
+        @Path("list-type")
+        @POST
+        public void listType(List<JaxbBeanType> b) {
+        }
+
+        @Path("array-bean")
+        @POST
+        public void arrayBean(JaxbBean[] b) {
+        }
+
+        @Path("array-type")
+        @POST
+        public void arrayType(JaxbBeanType[] b) {
+        }
+
+    }
+
+    public static class EmptyRequestTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(Resource.class).register(new JettisonFeature());
+        }
+
+        @Override
+        protected void configureClient(ClientConfig config) {
+            config.register(JettisonFeature.class);
+        }
+
+        @Test
+        public void testEmptyJsonRequestMapped() {
+            _test(target());
+        }
+
+        @Test
+        public void testEmptyXmlRequest() {
+            WebTarget r = target();
+
+            Response cr = r.request().post(Entity.entity(null, "application/xml"));
+            assertEquals(400, cr.getStatus());
+
+            cr = r.path("type").request().post(Entity.entity(null, "application/xml"));
+            assertEquals(400, cr.getStatus());
+
+            cr = r.path("list-bean").request().post(Entity.entity(null, "application/xml"));
+            assertEquals(400, cr.getStatus());
+
+            cr = r.path("list-type").request().post(Entity.entity(null, "application/xml"));
+            assertEquals(400, cr.getStatus());
+
+            cr = r.path("array-bean").request().post(Entity.entity(null, "application/xml"));
+            assertEquals(400, cr.getStatus());
+
+            cr = r.path("array-type").request().post(Entity.entity(null, "application/xml"));
+            assertEquals(400, cr.getStatus());
+        }
+    }
+
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public abstract static class CR implements ContextResolver<JAXBContext> {
+
+        private final JAXBContext context;
+
+        private final Class[] classes = {JaxbBean.class, JaxbBeanType.class};
+
+        private final Set<Class> types = new HashSet<>(Arrays.asList(classes));
+
+        public CR() {
+            try {
+                context = configure(classes);
+            } catch (JAXBException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        protected abstract JAXBContext configure(Class[] classes) throws JAXBException;
+
+        public JAXBContext getContext(Class<?> objectType) {
+            return (types.contains(objectType)) ? context : null;
+        }
+    }
+
+    public static class MappedJettisonCRTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(MappedJettisonCR.class, Resource.class).register(new JettisonFeature());
+        }
+
+        @Override
+        protected void configureClient(ClientConfig config) {
+            config.register(JettisonFeature.class);
+        }
+
+        public static class MappedJettisonCR extends CR {
+
+            protected JAXBContext configure(Class[] classes) throws JAXBException {
+                return new JettisonJaxbContext(JettisonConfig.mappedJettison().build(), classes);
+            }
+        }
+
+        @Test
+        public void testMappedJettisonCR() {
+            _test(target());
+        }
+    }
+
+    public static class BadgerFishCRTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(BadgerFishCR.class, Resource.class).register(new JettisonFeature());
+        }
+
+        @Override
+        protected void configureClient(ClientConfig config) {
+            config.register(JettisonFeature.class);
+        }
+
+        public static class BadgerFishCR extends CR {
+
+            protected JAXBContext configure(Class[] classes) throws JAXBException {
+                return new JettisonJaxbContext(JettisonConfig.badgerFish().build(), classes);
+            }
+        }
+
+        @Test
+        public void testBadgerFishCR() {
+            _test(target());
+        }
+    }
+
+    public static void _test(WebTarget target) {
+        Response cr = target.request().post(Entity.entity(null, "application/json"));
+        assertEquals(400, cr.getStatus());
+
+        cr = target.path("type").request().post(Entity.entity(null, "application/json"));
+        assertEquals(400, cr.getStatus());
+
+        cr = target.path("list-bean").request().post(Entity.entity(null, "application/json"));
+        assertEquals(400, cr.getStatus());
+
+        cr = target.path("list-type").request().post(Entity.entity(null, "application/json"));
+        assertEquals(400, cr.getStatus());
+
+        cr = target.path("array-bean").request().post(Entity.entity(null, "application/json"));
+        assertEquals(400, cr.getStatus());
+
+        cr = target.path("array-type").request().post(Entity.entity(null, "application/json"));
+        assertEquals(400, cr.getStatus());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
new file mode 100644
index 0000000..39ed8f2
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
@@ -0,0 +1,1166 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.activation.DataSource;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.message.internal.FileProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+@RunWith(ConcurrentRunner.class)
+public class EntityTypesTest extends AbstractTypeTester {
+
+    @Path("InputStreamResource")
+    public static class InputStreamResource {
+
+        @POST
+        public InputStream post(final InputStream in) throws IOException {
+            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            int read;
+            final byte[] data = new byte[2048];
+            while ((read = in.read(data)) != -1) {
+                out.write(data, 0, read);
+            }
+
+            return new ByteArrayInputStream(out.toByteArray());
+        }
+    }
+
+    @Test
+    public void testInputStream() {
+        final ByteArrayInputStream in = new ByteArrayInputStream("CONTENT".getBytes());
+        _test(in, InputStreamResource.class);
+    }
+
+    @Path("StringResource")
+    public static class StringResource extends AResource<String> {
+    }
+
+    @Test
+    public void testString() {
+        _test("CONTENT", StringResource.class);
+    }
+
+    @Path("DataSourceResource")
+    public static class DataSourceResource extends AResource<DataSource> {
+    }
+
+    @Path("ByteArrayResource")
+    public static class ByteArrayResource extends AResource<byte[]> {
+    }
+
+    @Test
+    public void testByteArrayRepresentation() {
+        _test("CONTENT".getBytes(), ByteArrayResource.class);
+    }
+
+    @Path("JaxbBeanResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JaxbBeanResource extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentation() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanResource.class, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Path("JaxbBeanResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JaxbBeanResourceMediaType extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationMediaType() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanResourceMediaType.class, MediaType.valueOf("application/foo+xml"));
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationError() {
+        final WebTarget target = target("JaxbBeanResource");
+
+        final String xml = "<root>foo</root>";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("JaxbBeanTextResource")
+    @Produces("text/xml")
+    @Consumes("text/xml")
+    public static class JaxbBeanTextResource extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanTextRepresentation() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanTextResource.class, MediaType.TEXT_XML_TYPE);
+    }
+
+    @Path("JAXBElementBeanResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBElementBeanResource extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentation() {
+        _test(new JaxbBean("CONTENT"), JAXBElementBeanResource.class, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Path("JAXBElementListResource")
+    @Produces({"application/xml", "application/json"})
+    @Consumes({"application/xml", "application/json"})
+    public static class JAXBElementListResource extends AResource<List<JAXBElement<String>>> {
+    }
+
+    private List<JAXBElement<String>> getJAXBElementList() {
+        return Arrays.asList(getJAXBElementArray());
+    }
+
+    @Test
+    public void testJAXBElementListXMLRepresentation() {
+        _testListOrArray(true, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    public void _testListOrArray(final boolean isList, final MediaType mt) {
+        final Object in = isList ? getJAXBElementList() : getJAXBElementArray();
+        final GenericType gt = isList ? new GenericType<List<JAXBElement<String>>>() {
+        } : new GenericType<JAXBElement<String>[]>() {
+        };
+
+        final WebTarget target = target(isList ? "JAXBElementListResource" : "JAXBElementArrayResource");
+        final Object out = target.request(mt).post(Entity.entity(new GenericEntity(in, gt.getType()), mt), gt);
+
+        final List<JAXBElement<String>> inList = isList ? ((List<JAXBElement<String>>) in) : Arrays
+                .asList((JAXBElement<String>[]) in);
+        final List<JAXBElement<String>> outList = isList ? ((List<JAXBElement<String>>) out) : Arrays
+                .asList((JAXBElement<String>[]) out);
+        assertEquals("Lengths differ", inList.size(), outList.size());
+        for (int i = 0; i < inList.size(); i++) {
+            assertEquals("Names of elements at index " + i + " differ", inList.get(i).getName(), outList.get(i).getName());
+            assertEquals("Values of elements at index " + i + " differ", inList.get(i).getValue(), outList.get(i).getValue());
+        }
+    }
+
+    @Test
+    public void testJAXBElementListJSONRepresentation() {
+        _testListOrArray(true, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Path("JAXBElementArrayResource")
+    @Produces({"application/xml", "application/json"})
+    @Consumes({"application/xml", "application/json"})
+    public static class JAXBElementArrayResource extends AResource<JAXBElement<String>[]> {
+    }
+
+    private JAXBElement<String>[] getJAXBElementArray() {
+        return new JAXBElement[] {
+                new JAXBElement(QName.valueOf("element1"), String.class, "ahoj"),
+                new JAXBElement(QName.valueOf("element2"), String.class, "nazdar")
+        };
+    }
+
+    @Test
+    public void testJAXBElementArrayXMLRepresentation() {
+        _testListOrArray(false, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Test
+    public void testJAXBElementArrayJSONRepresentation() {
+        _testListOrArray(false, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Path("JAXBElementBeanResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBElementBeanResourceMediaType extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationMediaType() {
+        _test(new JaxbBean("CONTENT"), JAXBElementBeanResourceMediaType.class, MediaType.valueOf("application/foo+xml"));
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationError() {
+        final WebTarget target = target("JAXBElementBeanResource");
+
+        final String xml = "<root><value>foo";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("JAXBElementBeanTextResource")
+    @Produces("text/xml")
+    @Consumes("text/xml")
+    public static class JAXBElementBeanTextResource extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanTextRepresentation() {
+        _test(new JaxbBean("CONTENT"), JAXBElementBeanTextResource.class, MediaType.TEXT_XML_TYPE);
+    }
+
+    @Path("JaxbBeanResourceAtom")
+    @Produces("application/atom+xml")
+    @Consumes("application/atom+xml")
+    public static class JaxbBeanResourceAtom extends AResource<JAXBElement<JaxbBean>> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationMediaTypeAtom() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanResourceAtom.class, MediaType.valueOf("application/atom+xml"));
+    }
+
+    @Path("JAXBTypeResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBTypeResource {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return ((ResourceConfig) super.configure()).register(new JettisonFeature());
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        super.configureClient(config);
+        config.register(new JettisonFeature());
+    }
+
+    @Test
+    public void testJAXBTypeRepresentation() {
+        final WebTarget target = target("JAXBTypeResource");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/xml"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBTypeResourceMediaType extends JAXBTypeResource {
+    }
+
+    @Test
+    public void testJAXBTypeRepresentationMediaType() {
+        final WebTarget target = target("JAXBTypeResourceMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/foo+xml"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBObjectResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBObjectResource {
+
+        @POST
+        public Object post(final Object o) {
+            return o;
+        }
+    }
+
+    @Provider
+    public static class JAXBObjectResolver implements ContextResolver<JAXBContext> {
+
+        public JAXBContext getContext(final Class<?> c) {
+            if (Object.class == c) {
+                try {
+                    return JAXBContext.newInstance(JaxbBean.class);
+                } catch (final JAXBException ex) {
+                }
+            }
+            return null;
+        }
+    }
+
+    @Test
+    public void testJAXBObjectRepresentation() {
+        final WebTarget target = target("JAXBObjectResource");
+        final Object in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/xml"), JaxbBean.class);
+        assertEquals(in, out);
+    }
+
+    @Path("JAXBObjectResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBObjectResourceMediaType extends JAXBObjectResource {
+    }
+
+    @Test
+    public void testJAXBObjectRepresentationMediaType() {
+        final WebTarget target = target("JAXBObjectResourceMediaType");
+        final Object in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/foo+xml"), JaxbBean.class);
+        assertEquals(in, out);
+    }
+
+    @Test
+    public void testJAXBObjectRepresentationError() {
+        final WebTarget target = target("JAXBObjectResource");
+
+        final String xml = "<root>foo</root>";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("FileResource")
+    public static class FileResource extends AResource<File> {
+    }
+
+    @Test
+    public void testFileRepresentation() throws IOException {
+        final FileProvider fp = new FileProvider();
+        final File in = fp.readFrom(File.class, File.class, null, null, null,
+                new ByteArrayInputStream("CONTENT".getBytes()));
+
+        _test(in, FileResource.class);
+    }
+
+    @Produces("application/x-www-form-urlencoded")
+    @Consumes("application/x-www-form-urlencoded")
+    @Path("FormResource")
+    public static class FormResource extends AResource<Form> {
+    }
+
+    @Test
+    public void testFormRepresentation() {
+        final Form fp = new Form();
+        fp.param("Email", "johndoe@gmail.com");
+        fp.param("Passwd", "north 23AZ");
+        fp.param("service", "cl");
+        fp.param("source", "Gulp-CalGul-1.05");
+
+        final WebTarget target = target("FormResource");
+        final Form response = target.request().post(Entity.entity(fp, MediaType.APPLICATION_FORM_URLENCODED_TYPE), Form.class);
+
+        assertEquals(fp.asMap().size(), response.asMap().size());
+        for (final Map.Entry<String, List<String>> entry : fp.asMap().entrySet()) {
+            final List<String> s = response.asMap().get(entry.getKey());
+            assertEquals(entry.getValue().size(), s.size());
+            for (Iterator<String> it1 = entry.getValue().listIterator(), it2 = s.listIterator(); it1.hasNext(); ) {
+                assertEquals(it1.next(), it2.next());
+            }
+        }
+    }
+
+    @Produces("application/json")
+    @Consumes("application/json")
+    @Path("JSONObjectResource")
+    public static class JSONObjectResource extends AResource<JSONObject> {
+    }
+
+    @Test
+    public void testJSONObjectRepresentation() throws Exception {
+        final JSONObject object = new JSONObject();
+        object.put("userid", 1234)
+                .put("username", "1234")
+                .put("email", "a@b")
+                .put("password", "****");
+
+        _test(object, JSONObjectResource.class, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Produces("application/xxx+json")
+    @Consumes("application/xxx+json")
+    @Path("JSONObjectResourceGeneralMediaType")
+    public static class JSONObjectResourceGeneralMediaType extends AResource<JSONObject> {
+    }
+
+    @Test
+    public void testJSONObjectRepresentationGeneralMediaTyp() throws Exception {
+        final JSONObject object = new JSONObject();
+        object.put("userid", 1234)
+                .put("username", "1234")
+                .put("email", "a@b")
+                .put("password", "****");
+
+        _test(object, JSONObjectResourceGeneralMediaType.class, MediaType.valueOf("application/xxx+json"));
+    }
+
+    @Produces("application/json")
+    @Consumes("application/json")
+    @Path("JSONOArrayResource")
+    public static class JSONOArrayResource extends AResource<JSONArray> {
+    }
+
+    @Test
+    public void testJSONArrayRepresentation() throws Exception {
+        final JSONArray array = new JSONArray();
+        array.put("One").put("Two").put("Three").put(1).put(2.0);
+
+        _test(array, JSONOArrayResource.class, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Produces("application/xxx+json")
+    @Consumes("application/xxx+json")
+    @Path("JSONOArrayResourceGeneralMediaType")
+    public static class JSONOArrayResourceGeneralMediaType extends AResource<JSONArray> {
+    }
+
+    @Test
+    public void testJSONArrayRepresentationGeneralMediaType() throws Exception {
+        final JSONArray array = new JSONArray();
+        array.put("One").put("Two").put("Three").put(1).put(2.0);
+
+        _test(array, JSONOArrayResourceGeneralMediaType.class, MediaType.valueOf("application/xxx+json"));
+    }
+
+    //    @Path("FeedResource")
+    //    public static class FeedResource extends AResource<Feed> {
+    //    }
+    //
+    //    @Test
+    //    public void testFeedRepresentation() throws Exception {
+    //        InputStream in = this.getClass().getResourceAsStream("feed.xml");
+    //        AtomFeedProvider afp = new AtomFeedProvider();
+    //        Feed f = afp.readFrom(Feed.class, Feed.class, null, null, null, in);
+    //
+    //        _test(f, FeedResource.class);
+    //    }
+    //
+    //    @Path("EntryResource")
+    //    public static class EntryResource extends AResource<Entry> {
+    //    }
+    //
+    //    @Test
+    //    public void testEntryRepresentation() throws Exception {
+    //        InputStream in = this.getClass().getResourceAsStream("entry.xml");
+    //        AtomEntryProvider afp = new AtomEntryProvider();
+    //        Entry e = afp.readFrom(Entry.class, Entry.class, null, null, null, in);
+    //
+    //        _test(e, EntryResource.class);
+    //    }
+
+    @Path("ReaderResource")
+    public static class ReaderResource extends AResource<Reader> {
+    }
+
+    @Test
+    public void testReaderRepresentation() throws Exception {
+        _test(new StringReader("CONTENT"), ReaderResource.class);
+    }
+
+    private static final String XML_DOCUMENT = "<n:x xmlns:n=\"urn:n\"><n:e>CONTNET</n:e></n:x>";
+
+    @Path("StreamSourceResource")
+    public static class StreamSourceResource extends AResource<StreamSource> {
+    }
+
+    @Test
+    public void testStreamSourceRepresentation() throws Exception {
+        final StreamSource ss = new StreamSource(
+                new ByteArrayInputStream(XML_DOCUMENT.getBytes()));
+        _test(ss, StreamSourceResource.class);
+    }
+
+    @Path("SAXSourceResource")
+    public static class SAXSourceResource extends AResource<SAXSource> {
+    }
+
+    @Test
+    public void testSAXSourceRepresentation() throws Exception {
+        final StreamSource ss = new StreamSource(
+                new ByteArrayInputStream(XML_DOCUMENT.getBytes()));
+        _test(ss, SAXSourceResource.class);
+    }
+
+    @Path("DOMSourceResource")
+    public static class DOMSourceResource extends AResource<DOMSource> {
+    }
+
+    @Test
+    public void testDOMSourceRepresentation() throws Exception {
+        final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        final Document d = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(XML_DOCUMENT)));
+        final DOMSource ds = new DOMSource(d);
+        _test(ds, DOMSourceResource.class);
+    }
+
+    @Path("DocumentResource")
+    public static class DocumentResource extends AResource<Document> {
+    }
+
+    @Test
+    public void testDocumentRepresentation() throws Exception {
+        final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        final Document d = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(XML_DOCUMENT)));
+        _test(d, DocumentResource.class);
+    }
+
+    @Path("FormMultivaluedMapResource")
+    @Produces("application/x-www-form-urlencoded")
+    @Consumes("application/x-www-form-urlencoded")
+    public static class FormMultivaluedMapResource {
+
+        @POST
+        public MultivaluedMap<String, String> post(final MultivaluedMap<String, String> t) {
+            return t;
+        }
+    }
+
+    @Test
+    public void testFormMultivaluedMapRepresentation() {
+        final MultivaluedMap<String, String> fp = new MultivaluedStringMap();
+        fp.add("Email", "johndoe@gmail.com");
+        fp.add("Passwd", "north 23AZ");
+        fp.add("service", "cl");
+        fp.add("source", "Gulp-CalGul-1.05");
+        fp.add("source", "foo.java");
+        fp.add("source", "bar.java");
+
+        final WebTarget target = target("FormMultivaluedMapResource");
+        final MultivaluedMap _fp = target.request()
+                .post(Entity.entity(fp, "application/x-www-form-urlencoded"), MultivaluedMap.class);
+        assertEquals(fp, _fp);
+    }
+
+    @Path("StreamingOutputResource")
+    public static class StreamingOutputResource {
+
+        @GET
+        public StreamingOutput get() {
+            return new StreamingOutput() {
+                public void write(final OutputStream entity) throws IOException {
+                    entity.write("CONTENT".getBytes());
+                }
+            };
+        }
+    }
+
+    @Test
+    public void testStreamingOutputRepresentation() throws Exception {
+        final WebTarget target = target("StreamingOutputResource");
+        assertEquals("CONTENT", target.request().get(String.class));
+    }
+
+    @Path("JAXBElementBeanJSONResource")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class JAXBElementBeanJSONResource extends AResource<JAXBElement<String>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanJSONRepresentation() {
+        final WebTarget target = target("JAXBElementBeanJSONResource");
+
+        final Response rib = target.request().post(
+                Entity.entity(new JAXBElement<>(new QName("test"), String.class, "CONTENT"), "application/json"));
+
+        // TODO: the following would not be needed if i knew how to workaround JAXBElement<String>.class literal
+
+        final byte[] inBytes = getRequestEntity();
+        final byte[] outBytes = getEntityAsByteArray(rib);
+
+        assertEquals(new String(outBytes), inBytes.length, outBytes.length);
+        for (int i = 0; i < inBytes.length; i++) {
+            if (inBytes[i] != outBytes[i]) {
+                assertEquals("Index: " + i, inBytes[i], outBytes[i]);
+            }
+        }
+    }
+
+    @Path("JaxbBeanResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JaxbBeanResourceJSON extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationJSON() {
+        final WebTarget target = target("JaxbBeanResourceJSON");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JaxbBeanResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JaxbBeanResourceJSONMediaType extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationJSONMediaType() {
+        final WebTarget target = target("JaxbBeanResourceJSONMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/foo+json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBElementBeanResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JAXBElementBeanResourceJSON extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationJSON() {
+        final WebTarget target = target("JAXBElementBeanResourceJSON");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBElementBeanResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JAXBElementBeanResourceJSONMediaType extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationJSONMediaType() {
+        final WebTarget target = target("JAXBElementBeanResourceJSONMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/foo+json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JAXBTypeResourceJSON {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Test
+    public void testJAXBTypeRepresentationJSON() {
+        final WebTarget target = target("JAXBTypeResourceJSON");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/json"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JAXBTypeResourceJSONMediaType {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Test
+    public void testJAXBTypeRepresentationJSONMediaType() {
+        final WebTarget target = target("JAXBTypeResourceJSONMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/foo+json"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JaxbBeanResourceFastInfoset")
+    @Produces("application/fastinfoset")
+    @Consumes("application/fastinfoset")
+    public static class JaxbBeanResourceFastInfoset extends AResource<JaxbBean> {
+    }
+
+    @Test
+    @Ignore("TODO: unignore once fi support implemented (JERSEY-1190)")
+    // TODO: unignore once fi support implemented (JERSEY-1190)
+    public void testJaxbBeanRepresentationFastInfoset() {
+        final WebTarget target = target("JaxbBeanResourceFastInfoset");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/fastinfoset"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBElementBeanResourceFastInfoset")
+    @Produces("application/fastinfoset")
+    @Consumes("application/fastinfoset")
+    public static class JAXBElementBeanResourceFastInfoset extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    @Ignore("TODO: unignore once fi support implemented (JERSEY-1190)")
+    // TODO: unignore once fi support implemented (JERSEY-1190)
+    public void testJAXBElementBeanRepresentationFastInfoset() {
+        final WebTarget target = target("JAXBElementBeanResourceFastInfoset");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/fastinfoset"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceFastInfoset")
+    @Produces("application/fastinfoset")
+    @Consumes("application/fastinfoset")
+    public static class JAXBTypeResourceFastInfoset {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Test
+    @Ignore("TODO: unignore once fi support implemented (JERSEY-1190)")
+    // TODO: unignore once fi support implemented (JERSEY-1190)
+    public void testJAXBTypeRepresentationFastInfoset() {
+        final WebTarget target = target("JAXBTypeResourceFastInfoset");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/fastinfoset"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBListResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBListResource {
+
+        @POST
+        public List<JaxbBean> post(final List<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("set")
+        public Set<JaxbBean> postSet(final Set<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("queue")
+        public Queue<JaxbBean> postQueue(final Queue<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("stack")
+        public Stack<JaxbBean> postStack(final Stack<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("custom")
+        public MyArrayList<JaxbBean> postCustom(final MyArrayList<JaxbBean> l) {
+            return l;
+        }
+
+        @GET
+        public Collection<JaxbBean> get() {
+            final ArrayList<JaxbBean> l = new ArrayList<>();
+            l.add(new JaxbBean("one"));
+            l.add(new JaxbBean("two"));
+            l.add(new JaxbBean("three"));
+            return l;
+        }
+
+        @POST
+        @Path("type")
+        public List<JaxbBean> postType(final Collection<JaxbBeanType> l) {
+            final List<JaxbBean> beans = new ArrayList<>();
+            for (final JaxbBeanType t : l) {
+                beans.add(new JaxbBean(t.value));
+            }
+            return beans;
+        }
+    }
+
+    @Path("JAXBArrayResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBArrayResource {
+
+        @POST
+        public JaxbBean[] post(final JaxbBean[] l) {
+            return l;
+        }
+
+        @GET
+        public JaxbBean[] get() {
+            final ArrayList<JaxbBean> l = new ArrayList<>();
+            l.add(new JaxbBean("one"));
+            l.add(new JaxbBean("two"));
+            l.add(new JaxbBean("three"));
+            return l.toArray(new JaxbBean[l.size()]);
+        }
+
+        @POST
+        @Path("type")
+        public JaxbBean[] postType(final JaxbBeanType[] l) {
+            final List<JaxbBean> beans = new ArrayList<>();
+            for (final JaxbBeanType t : l) {
+                beans.add(new JaxbBean(t.value));
+            }
+            return beans.toArray(new JaxbBean[beans.size()]);
+        }
+    }
+
+    @Test
+    public void testJAXBArrayRepresentation() {
+        final WebTarget target = target("JAXBArrayResource");
+
+        final JaxbBean[] a = target.request().get(JaxbBean[].class);
+        JaxbBean[] b = target.request().post(Entity.entity(a, "application/xml"), JaxbBean[].class);
+        assertEquals(a.length, b.length);
+        for (int i = 0; i < a.length; i++) {
+            assertEquals(a[i], b[i]);
+        }
+
+        b = target.path("type").request().post(Entity.entity(a, "application/xml"), JaxbBean[].class);
+        assertEquals(a.length, b.length);
+        for (int i = 0; i < a.length; i++) {
+            assertEquals(a[i], b[i]);
+        }
+    }
+
+    @Path("JAXBListResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBListResourceMediaType extends JAXBListResource {
+    }
+
+    @Test
+    public void testJAXBListRepresentationMediaType() {
+        final WebTarget target = target("JAXBListResourceMediaType");
+
+        Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        Collection<JaxbBean> b = target.request()
+                .post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+                        }, "application/foo+xml"),
+                        new GenericType<Collection<JaxbBean>>() {
+                        });
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/foo+xml"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new LinkedList<>(a);
+        b = target.path("queue").request().post(Entity.entity(new GenericEntity<Queue<JaxbBean>>((Queue<JaxbBean>) a) {
+        }, "application/foo+xml"), new GenericType<Queue<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new HashSet<>(a);
+        b = target.path("set").request().post(Entity.entity(new GenericEntity<Set<JaxbBean>>((Set<JaxbBean>) a) {
+        }, "application/foo+xml"), new GenericType<Set<JaxbBean>>() {
+        });
+        final Comparator<JaxbBean> c = new Comparator<JaxbBean>() {
+            @Override
+            public int compare(final JaxbBean t, final JaxbBean t1) {
+                return t.value.compareTo(t1.value);
+            }
+        };
+        final TreeSet<JaxbBean> t1 = new TreeSet<>(c);
+        final TreeSet<JaxbBean> t2 = new TreeSet<>(c);
+        t1.addAll(a);
+        t2.addAll(b);
+        assertEquals(t1, t2);
+
+        final Stack<JaxbBean> s = new Stack<>();
+        s.addAll(a);
+        b = target.path("stack").request().post(Entity.entity(new GenericEntity<Stack<JaxbBean>>(s) {
+        }, "application/foo+xml"), new GenericType<Stack<JaxbBean>>() {
+        });
+        assertEquals(s, b);
+
+        a = new MyArrayList<>(a);
+        b = target.path("custom").request()
+                .post(Entity.entity(new GenericEntity<MyArrayList<JaxbBean>>((MyArrayList<JaxbBean>) a) {
+                }, "application/foo+xml"), new GenericType<MyArrayList<JaxbBean>>() {
+                });
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testJAXBListRepresentationError() {
+        final WebTarget target = target("JAXBListResource");
+
+        final String xml = "<root><value>foo";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("JAXBListResourceFastInfoset")
+    @Produces("application/fastinfoset")
+    @Consumes("application/fastinfoset")
+    public static class JAXBListResourceFastInfoset extends JAXBListResource {
+    }
+
+    /**
+     * TODO, the unmarshalling fails.
+     */
+    @Test
+    @Ignore("TODO: unignore once fi support implemented (JERSEY-1190)")
+    // TODO: unignore once fi support implemented (JERSEY-1190)
+    public void testJAXBListRepresentationFastInfoset() {
+        final WebTarget target = target("JAXBListResourceFastInfoset");
+
+        Collection<JaxbBean> a = target.request().get(new GenericType<Collection<JaxbBean>>() {
+        });
+
+        Collection<JaxbBean> b = target.request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+                }, "application/fastinfoset"), new GenericType<Collection<JaxbBean>>() {
+                }
+        );
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/fastinfoset"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new LinkedList<>(a);
+        b = target.path("queue").request().post(Entity.entity(new GenericEntity<Queue<JaxbBean>>((Queue<JaxbBean>) a) {
+        }, "application/fastinfoset"), new GenericType<Queue<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new HashSet<>(a);
+        b = target.path("set").request().post(Entity.entity(new GenericEntity<Set<JaxbBean>>((Set<JaxbBean>) a) {
+        }, "application/fastinfoset"), new GenericType<Set<JaxbBean>>() {
+        });
+        final Comparator<JaxbBean> c = new Comparator<JaxbBean>() {
+            @Override
+            public int compare(final JaxbBean t, final JaxbBean t1) {
+                return t.value.compareTo(t1.value);
+            }
+        };
+        final TreeSet<JaxbBean> t1 = new TreeSet<>(c);
+        final TreeSet<JaxbBean> t2 = new TreeSet<>(c);
+        t1.addAll(a);
+        t2.addAll(b);
+        assertEquals(t1, t2);
+
+        final Stack<JaxbBean> s = new Stack<>();
+        s.addAll(a);
+        b = target.path("stack").request().post(Entity.entity(new GenericEntity<Stack<JaxbBean>>(s) {
+        }, "application/fastinfoset"), new GenericType<Stack<JaxbBean>>() {
+        });
+        assertEquals(s, b);
+
+        a = new MyArrayList<>(a);
+        b = target.path("custom").request()
+                .post(Entity.entity(new GenericEntity<MyArrayList<JaxbBean>>((MyArrayList<JaxbBean>) a) {
+                }, "application/fastinfoset"), new GenericType<MyArrayList<JaxbBean>>() {
+                });
+        assertEquals(a, b);
+    }
+
+    @Path("JAXBListResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JAXBListResourceJSON extends JAXBListResource {
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSON() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSON");
+
+        Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        Collection<JaxbBean> b = target.request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/json"), new GenericType<Collection<JaxbBean>>() {
+        });
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/json"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new LinkedList<>(a);
+        b = target.path("queue").request().post(Entity.entity(new GenericEntity<Queue<JaxbBean>>((Queue<JaxbBean>) a) {
+        }, "application/json"), new GenericType<Queue<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new HashSet<>(a);
+        b = target.path("set").request().post(Entity.entity(new GenericEntity<Set<JaxbBean>>((Set<JaxbBean>) a) {
+        }, "application/json"), new GenericType<Set<JaxbBean>>() {
+        });
+        final Comparator<JaxbBean> c = new Comparator<JaxbBean>() {
+            @Override
+            public int compare(final JaxbBean t, final JaxbBean t1) {
+                return t.value.compareTo(t1.value);
+            }
+        };
+        final TreeSet<JaxbBean> t1 = new TreeSet<>(c);
+        final TreeSet<JaxbBean> t2 = new TreeSet<>(c);
+        t1.addAll(a);
+        t2.addAll(b);
+        assertEquals(t1, t2);
+
+        final Stack<JaxbBean> s = new Stack<>();
+        s.addAll(a);
+        b = target.path("stack").request().post(Entity.entity(new GenericEntity<Stack<JaxbBean>>(s) {
+        }, "application/json"), new GenericType<Stack<JaxbBean>>() {
+        });
+        assertEquals(s, b);
+
+        a = new MyArrayList<>(a);
+        b = target.path("custom").request()
+                .post(Entity.entity(new GenericEntity<MyArrayList<JaxbBean>>((MyArrayList<JaxbBean>) a) {
+                }, "application/json"), new GenericType<MyArrayList<JaxbBean>>() {
+                });
+        assertEquals(a, b);
+
+        // TODO: would be nice to produce/consume a real JSON array like following
+        // instead of what we have now:
+        //        JSONArray a = r.get(JSONArray.class);
+        //        JSONArray b = new JSONArray().
+        //                put(new JSONObject().put("value", "one")).
+        //                put(new JSONObject().put("value", "two")).
+        //                put(new JSONObject().put("value", "three"));
+        //        assertEquals(a.toString(), b.toString());
+        //        JSONArray c = r.post(JSONArray.class, b);
+        //        assertEquals(a.toString(), c.toString());
+    }
+
+    @Path("JAXBListResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JAXBListResourceJSONMediaType extends JAXBListResource {
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSONMediaType() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSONMediaType");
+
+        final Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        Collection<JaxbBean> b = target.request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/foo+json"), new GenericType<Collection<JaxbBean>>() {
+        });
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/foo+json"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        // TODO: would be nice to produce/consume a real JSON array like following
+        // instead of what we have now:
+        //        JSONArray a = r.get(JSONArray.class);
+        //        JSONArray b = new JSONArray().
+        //                put(new JSONObject().put("value", "one")).
+        //                put(new JSONObject().put("value", "two")).
+        //                put(new JSONObject().put("value", "three"));
+        //        assertEquals(a.toString(), b.toString());
+        //        JSONArray c = r.post(JSONArray.class, b);
+        //        assertEquals(a.toString(), c.toString());
+    }
+
+    @Path("/NoContentTypeJAXBResource")
+    public static class NoContentTypeJAXBResource {
+
+        @POST
+        public JaxbBean post(@Context final HttpHeaders headers, final JaxbBean bean) {
+            assertThat(headers.getMediaType(), is(MediaType.APPLICATION_XML_TYPE));
+            return bean;
+        }
+    }
+
+    @Test
+    public void testNoContentTypeJaxbEntity() throws IOException {
+        assertThat(target("NoContentTypeJAXBResource").request("application/xml").post(Entity.xml(new JaxbBean("foo")))
+                        .getMediaType(),
+                is(MediaType.APPLICATION_XML_TYPE));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/GenericTypeAndEntityTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/GenericTypeAndEntityTest.java
new file mode 100644
index 0000000..bed9281
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/GenericTypeAndEntityTest.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ *
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class GenericTypeAndEntityTest extends AbstractTypeTester {
+
+    @Provider
+    @SuppressWarnings("UnusedDeclaration")
+    public static class ListIntegerWriter implements MessageBodyReader<List<Integer>>, MessageBodyWriter<List<Integer>> {
+
+        private final Type t;
+
+        public ListIntegerWriter() {
+            final List<Integer> l = new ArrayList<>();
+            final GenericEntity<List<Integer>> ge = new GenericEntity<List<Integer>>(l) {};
+            this.t = ge.getType();
+        }
+
+        public boolean isWriteable(final Class<?> c, final Type t, final Annotation[] as, final MediaType mt) {
+            return this.t.equals(t);
+        }
+
+        public long getSize(final List<Integer> l, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        public void writeTo(final List<Integer> l, final Class<?> c, final Type t, final Annotation[] as,
+                            final MediaType mt, final MultivaluedMap<String, Object> hs,
+                            final OutputStream out) throws IOException, WebApplicationException {
+            final StringBuilder sb = new StringBuilder();
+            for (final Integer i : l) {
+                if (sb.length() > 0) {
+                    sb.append(", ");
+                }
+                sb.append(i);
+            }
+            out.write(sb.toString().getBytes());
+        }
+
+        @Override
+        public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                  final MediaType mediaType) {
+            return this.t.equals(genericType);
+        }
+
+        @Override
+        public List<Integer> readFrom(final Class<List<Integer>> type, final Type genericType, final Annotation[] annotations,
+                                      final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders,
+                                      final InputStream entityStream) throws IOException, WebApplicationException {
+            return Arrays.stream(ReaderWriter.readFromAsString(entityStream, mediaType).split(","))
+                         .map(input -> Integer.valueOf(input.trim()))
+                         .collect(Collectors.toList());
+        }
+    }
+
+    public static class GenericListResource<T> {
+
+        @POST
+        @Path("genericType")
+        public List<T> post(final List<T> post) {
+            return post;
+        }
+    }
+
+    @Path("ListResource")
+    public static class ListResource extends GenericListResource<Integer> {
+
+        @GET
+        @Path("type")
+        public List<Integer> type() {
+            return Arrays.asList(1, 2, 3, 4);
+        }
+
+        @GET
+        @Path("genericEntity")
+        public GenericEntity<List<Integer>> genericEntity() {
+            return new GenericEntity<List<Integer>>(Arrays.asList(1, 2, 3, 4)) {};
+        }
+
+        @GET
+        @Path("object")
+        public Object object() {
+            return new GenericEntity<List<Integer>>(Arrays.asList(1, 2, 3, 4)) {};
+        }
+
+        @GET
+        @Path("response")
+        public Response response() {
+            return Response.ok(new GenericEntity<List<Integer>>(Arrays.asList(1, 2, 3, 4)) {}).build();
+        }
+
+        @GET
+        @Path("wrongGenericEntity")
+        public GenericEntity<List<Integer>> wrongGenericEntity() {
+            // wrongly constructed generic entity: generic type of the generic entity
+            // is not generic but just a List interface type. In this case
+            // the return generic type will be used
+            return new GenericEntity<>(Arrays.asList(1, 2, 3, 4), List.class);
+        }
+    }
+
+    public static class GenericListMediaTypeResource<T> {
+
+        @POST
+        @Path("genericType")
+        @Produces("text/plain")
+        public List<T> post(final List<T> post) {
+            return post;
+        }
+    }
+
+    @Path("ListResourceWithMediaType")
+    public static class ListResourceWithMediaType extends GenericListMediaTypeResource<Integer> {
+
+        @GET
+        @Path("type")
+        @Produces("text/plain")
+        public List<Integer> type() {
+            return Arrays.asList(1, 2, 3, 4);
+        }
+
+        @GET
+        @Path("genericEntity")
+        @Produces("text/plain")
+        public GenericEntity<List<Integer>> genericEntity() {
+            return new GenericEntity<List<Integer>>(Arrays.asList(1, 2, 3, 4)) {};
+        }
+
+        @GET
+        @Path("object")
+        @Produces("text/plain")
+        public Object object() {
+            return new GenericEntity<List<Integer>>(Arrays.asList(1, 2, 3, 4)) {};
+        }
+
+        @GET
+        @Path("response")
+        @Produces("text/plain")
+        public Response response() {
+            return Response.ok(new GenericEntity<List<Integer>>(Arrays.asList(1, 2, 3, 4)) {}).build();
+        }
+
+        @GET
+        @Path("wrongGenericEntity")
+        @Produces("text/plain")
+        public GenericEntity<List<Integer>> wrongGenericEntity() {
+            // wrongly constructed generic entity: generic type of the generic entity
+            // is not generic but just a List interface type. In this case
+            // the return generic type will be used
+            return new GenericEntity<>(Arrays.asList(1, 2, 3, 4), List.class);
+        }
+    }
+
+    @Test
+    public void testGenericType() {
+        _genericTest(ListResource.class);
+    }
+
+    @Test
+    public void testGenericTypeWithMediaType() {
+        _genericTest(ListResourceWithMediaType.class);
+    }
+
+    private void _genericTest(final Class resourceClass) {
+        final WebTarget target = target(resourceClass.getSimpleName());
+
+        _testPath(target, "type");
+        _testPath(target, "genericEntity");
+        _testPath(target, "object");
+        _testPath(target, "response");
+        _testPath(target, "wrongGenericEntity");
+
+        _testPathPost(target, "genericType");
+    }
+
+    private void _testPath(final WebTarget target, final String path) {
+        assertEquals("1, 2, 3, 4", target.path(path).request().get(String.class));
+    }
+
+    private void _testPathPost(final WebTarget target, final String path) {
+        assertEquals("1, 2, 3, 4", target.path(path).request().post(Entity.text("1, 2, 3, 4"), String.class));
+    }
+
+    @Provider
+    public static class MapStringReader implements MessageBodyReader<Map<String, String>>,
+                                                   MessageBodyWriter<Map<String, String>> {
+
+        private final Type mapStringType;
+
+        public MapStringReader() {
+            final ParameterizedType iface = (ParameterizedType) this.getClass().getGenericInterfaces()[0];
+            mapStringType = iface.getActualTypeArguments()[0];
+        }
+
+        public boolean isReadable(final Class<?> c, final Type t, final Annotation[] as, final MediaType mt) {
+            return Map.class == c && mapStringType.equals(t);
+        }
+
+        public Map<String, String> readFrom(final Class<Map<String, String>> c, final Type t, final Annotation[] as,
+                                            final MediaType mt, final MultivaluedMap<String, String> headers,
+                                            final InputStream in) throws IOException {
+            final String[] v = ReaderWriter.readFromAsString(in, mt).split(",");
+            final Map<String, String> m = new LinkedHashMap<>();
+            for (int i = 0; i < v.length; i = i + 2) {
+                m.put(v[i], v[i + 1]);
+            }
+            return m;
+        }
+
+        @Override
+        public boolean isWriteable(final Class<?> c, final Type t, final Annotation[] as, final MediaType mt) {
+            return Map.class.isAssignableFrom(c) && mapStringType.equals(t);
+        }
+
+        @Override
+        public long getSize(final Map<String, String> t, final Class<?> type, final Type genericType, final Annotation[] as,
+                            final MediaType mt) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final Map<String, String> t, final Class<?> c, final Type genericType, final Annotation[] as,
+                            final MediaType mt, final MultivaluedMap<String, Object> hs,
+                            final OutputStream out) throws IOException, WebApplicationException {
+            final StringBuilder sb = new StringBuilder();
+            for (final Map.Entry<String, String> e : t.entrySet()) {
+                if (sb.length() > 0) {
+                    sb.append(',');
+                }
+                sb.append(e.getKey()).append(',').append(e.getValue());
+            }
+            out.write(sb.toString().getBytes());
+        }
+    }
+
+    public static class GenericMapResource<K, V> {
+
+        @POST
+        public Map<K, V> post(final Map<K, V> m) {
+            return m;
+        }
+    }
+
+    @Path("/MapResource")
+    public static class MapResource extends GenericMapResource<String, String> {
+
+    }
+
+    @Test
+    public void testGenericMap() throws Exception {
+        assertThat(target("/MapResource").request().post(Entity.text("a,b,c,d"), String.class), is("a,b,c,d"));
+    }
+
+    @Provider
+    public static class MapListStringReader implements MessageBodyReader<Map<String, List<String>>>,
+                                                       MessageBodyWriter<Map<String, List<String>>> {
+
+        private final Type mapListStringType;
+
+        public MapListStringReader() {
+            final ParameterizedType iface = (ParameterizedType) this.getClass().getGenericInterfaces()[0];
+            mapListStringType = iface.getActualTypeArguments()[0];
+        }
+
+        public boolean isReadable(final Class<?> c, final Type t, final Annotation[] as, final MediaType mt) {
+            return Map.class == c && mapListStringType.equals(t);
+        }
+
+        public Map<String, List<String>> readFrom(final Class<Map<String, List<String>>> c, final Type t,
+                                                  final Annotation[] as, final MediaType mt, final MultivaluedMap<String,
+                String> headers, final InputStream in) throws IOException {
+            try {
+                final JSONObject o = new JSONObject(ReaderWriter.readFromAsString(in, mt));
+
+                final Map<String, List<String>> m = new LinkedHashMap<>();
+                final Iterator keys = o.keys();
+                while (keys.hasNext()) {
+                    final String key = (String) keys.next();
+                    final List<String> l = new ArrayList<>();
+                    m.put(key, l);
+                    final JSONArray a = o.getJSONArray(key);
+                    for (int i = 0; i < a.length(); i++) {
+                        l.add(a.getString(i));
+                    }
+                }
+                return m;
+            } catch (final JSONException ex) {
+                throw new IOException(ex);
+            }
+        }
+
+        @Override
+        public boolean isWriteable(final Class<?> c, final Type t, final Annotation[] as, final MediaType mt) {
+            return Map.class.isAssignableFrom(c) && mapListStringType.equals(t);
+        }
+
+        @Override
+        public long getSize(final Map<String, List<String>> t, final Class<?> type, final Type genericType,
+                            final Annotation[] as, final MediaType mt) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final Map<String, List<String>> t, final Class<?> c, final Type genericType, final Annotation[] as,
+                            final MediaType mt, final MultivaluedMap<String, Object> hs,
+                            final OutputStream out) throws IOException, WebApplicationException {
+            try {
+                final JSONObject o = new JSONObject();
+                for (final Map.Entry<String, List<String>> e : t.entrySet()) {
+                    o.put(e.getKey(), e.getValue());
+                }
+                out.write(o.toString().getBytes());
+            } catch (final JSONException ex) {
+                throw new IOException(ex);
+            }
+        }
+    }
+
+    public static class GenericMapListResource<K, V> {
+
+        @POST
+        public Map<K, List<V>> post(final Map<K, List<V>> m) {
+            return m;
+        }
+    }
+
+    @Path("/MapListResource")
+    public static class MapListResource extends GenericMapListResource<String, String> {
+
+    }
+
+    @Test
+    public void testGenericMapList() throws Exception {
+        final String json = "{\"a\":[\"1\",\"2\"],\"b\":[\"1\",\"2\"]}";
+        assertThat(target("/MapListResource").request().post(Entity.text(json), String.class), is(json));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InjectedProviderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InjectedProviderTest.java
new file mode 100644
index 0000000..5562ed8
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InjectedProviderTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.client.ClientConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Paul Sandoz
+ */
+public class InjectedProviderTest extends AbstractTypeTester {
+
+    public static class Bean implements Serializable {
+
+        private String string;
+
+        public Bean() {
+        }
+
+        public Bean(String string) {
+            this.string = string;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+    }
+
+    @Provider
+    public static class BeanReader implements MessageBodyReader<Bean> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
+            return type == Bean.class;
+        }
+
+        @Override
+        public Bean readFrom(
+                Class<Bean> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException {
+            ObjectInputStream oin = new ObjectInputStream(entityStream);
+            try {
+                return (Bean) oin.readObject();
+            } catch (ClassNotFoundException cause) {
+                throw new IOException(cause);
+            }
+        }
+    }
+
+    @Provider
+    public static class InjectedBeanReaderWriter extends BeanReader implements MessageBodyWriter<Bean> {
+
+        @Context
+        UriInfo uriInfo;
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
+            return type == Bean.class;
+        }
+
+        @Override
+        public void writeTo(
+                Bean t,
+                Class<?> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException {
+            t.setString(uriInfo.getRequestUri().toString());
+            ObjectOutputStream out = new ObjectOutputStream(entityStream);
+            out.writeObject(t);
+            out.flush();
+        }
+
+        @Override
+        public long getSize(Bean t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+    }
+
+    @Path("/one/two/{id}")
+    public static class BeanResource {
+
+        @GET
+        public Bean get() {
+            return new Bean("");
+        }
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(BeanReader.class);
+    }
+
+    @Test
+    public void testBean() throws Exception {
+        Bean bean3 = target("one/two/three").request().get(Bean.class);
+        Bean bean4 = target("one/two/four").request().get(Bean.class);
+
+        final Map<String, String> map3 = new HashMap<String, String>() {{
+            put("id", "three");
+        }};
+        final Map<String, String> map4 = new HashMap<String, String>() {{
+            put("id", "four");
+        }};
+
+        String requestUri3 = target().getUriBuilder()
+                .path(BeanResource.class).buildFromMap(map3).toString();
+        String requestUri4 = target().getUriBuilder()
+                .path(BeanResource.class).buildFromMap(map4).toString();
+
+        assertEquals(requestUri3, bean3.getString());
+        assertEquals(requestUri4, bean4.getString());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InterceptedStreamCloseTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InterceptedStreamCloseTest.java
new file mode 100644
index 0000000..62ac577
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InterceptedStreamCloseTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity;
+
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer for JERSEY-1845.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class InterceptedStreamCloseTest extends JerseyTest {
+    @Path("resource")
+    public static class TestResource {
+        @POST
+        public String post(String entity) {
+            return entity;
+        }
+    }
+
+    public static class ClientInterceptor implements ReaderInterceptor, WriterInterceptor {
+        private volatile int readFromCount = 0;
+        private volatile int inCloseCount = 0;
+        private volatile int writeToCount = 0;
+        private volatile int outCloseCount = 0;
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            readFromCount++;
+
+            context.setInputStream(new FilterInputStream(context.getInputStream()) {
+                @Override
+                public void close() throws IOException {
+                    inCloseCount++;
+                    super.close();
+                }
+            });
+            return context.proceed();
+        }
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            writeToCount++;
+
+            context.setOutputStream(new FilterOutputStream(context.getOutputStream()) {
+                @Override
+                public void close() throws IOException {
+                    outCloseCount++;
+                    super.close();
+                }
+            });
+
+            context.proceed();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Test
+    public void testWrappedStreamClosed() {
+        ClientInterceptor interceptor = new ClientInterceptor();
+
+        final Response response = target().register(interceptor)
+                .path("resource").request().post(Entity.entity("entity", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals("entity", response.readEntity(String.class));
+
+        assertEquals(1, interceptor.readFromCount);
+        assertEquals(1, interceptor.inCloseCount);
+        assertEquals(1, interceptor.writeToCount);
+        assertEquals(1, interceptor.outCloseCount);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidEntityTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidEntityTest.java
new file mode 100644
index 0000000..5da7cc5
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidEntityTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.ResponseProcessingException;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Martin Matula
+ */
+public class InvalidEntityTest extends AbstractTypeTester {
+    @Path("/")
+    public static class TestResource {
+        @GET
+        @Produces("foo/bar")
+        public String getFooBar() {
+            return "foo/bar";
+        }
+    }
+
+    @Test
+    public void testInvalidEntity() {
+        Throwable exception = null;
+        try {
+            target().request("foo/bar").get(Integer.class);
+        } catch (Exception e) {
+            exception = e;
+        }
+        if (!(exception instanceof ResponseProcessingException)) {
+            if (exception != null) {
+                exception.printStackTrace();
+            }
+            fail();
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidFormTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidFormTest.java
new file mode 100644
index 0000000..6af9b30
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidFormTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Paul Sandoz
+ */
+public class InvalidFormTest extends AbstractTypeTester {
+
+    public abstract static class Resource<T> {
+
+        @POST
+        public void post(T t) {
+        }
+    }
+
+    @Path("multivaluedmap")
+    public static class FormMultivaluedMapResource extends Resource<MultivaluedMap<String, String>> {}
+
+    @Test
+    public void testFormMultivaluedMapRepresentation() {
+        _test("multivaluedmap");
+    }
+
+    @Path("form")
+    public static class FormResource extends Resource<Form> {}
+
+    @Test
+    public void testRepresentation() {
+        _test("form");
+    }
+
+    public void _test(String path) {
+        String form = "m=%";
+        Response cr = target(path).request().post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+        assertEquals(400, cr.getStatus());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JAXBContextResolverTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JAXBContextResolverTest.java
new file mode 100644
index 0000000..2f28775
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JAXBContextResolverTest.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+@RunWith(Enclosed.class)
+public class JAXBContextResolverTest {
+
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JAXBContextResolver implements ContextResolver<JAXBContext> {
+
+        private JAXBContext context;
+        private int invoked;
+
+        public JAXBContextResolver() {
+            try {
+                this.context = JAXBContext.newInstance(JaxbBean.class);
+            } catch (JAXBException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        public JAXBContext getContext(Class<?> c) {
+            if (JaxbBean.class == c) {
+                invoked++;
+                return context;
+            } else {
+                return null;
+            }
+        }
+
+        public int invoked() {
+            return invoked;
+        }
+    }
+
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class MarshallerResolver implements ContextResolver<Marshaller> {
+
+        private JAXBContext context;
+        private int invoked;
+
+        public MarshallerResolver() {
+            try {
+                this.context = JAXBContext.newInstance(JaxbBean.class);
+            } catch (JAXBException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        public Marshaller getContext(Class<?> c) {
+            if (JaxbBean.class == c) {
+                invoked++;
+                try {
+                    return context.createMarshaller();
+                } catch (JAXBException ex) {
+                    throw new RuntimeException(ex);
+                }
+            } else {
+                return null;
+            }
+        }
+
+        public int invoked() {
+            return invoked;
+        }
+    }
+
+    @Provider
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class UnmarshallerResolver implements ContextResolver<Unmarshaller> {
+
+        private JAXBContext context;
+        private int invoked;
+
+        public UnmarshallerResolver() {
+            try {
+                this.context = JAXBContext.newInstance(JaxbBean.class);
+            } catch (JAXBException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        public Unmarshaller getContext(Class<?> c) {
+            if (JaxbBean.class == c) {
+                invoked++;
+                try {
+                    return context.createUnmarshaller();
+                } catch (JAXBException ex) {
+                    throw new RuntimeException(ex);
+                }
+            } else {
+                return null;
+            }
+        }
+
+        public int invoked() {
+            return invoked;
+        }
+    }
+
+    @Path("/")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JaxbBeanResource {
+
+        @POST
+        public JaxbBean get(JaxbBean b) {
+            return b;
+        }
+    }
+
+    public static class JAXBContextTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            return new ResourceConfig(JaxbBeanResource.class).registerInstances(cr);
+        }
+
+        @Test
+        public void testJAXBContext() throws Exception {
+            final Response response = target().request(MediaType.APPLICATION_XML_TYPE).post(Entity.xml(new JaxbBean("foo")));
+            assertThat(response.getMediaType(), equalTo(MediaType.APPLICATION_XML_TYPE));
+
+            final JaxbBean foo = response.readEntity(JaxbBean.class);
+            assertThat(foo.value, equalTo("foo"));
+            assertThat(cr.invoked(), equalTo(2));
+        }
+    }
+
+    public static class UnmarshallerTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private MarshallerResolver mr;
+        private UnmarshallerResolver umr;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            mr = new MarshallerResolver();
+            umr = new UnmarshallerResolver();
+            return new ResourceConfig(JaxbBeanResource.class).registerInstances(cr, mr, umr);
+        }
+
+        @Test
+        public void testUnmarshaller() throws Exception {
+            final Response response = target().request(MediaType.APPLICATION_XML_TYPE).post(Entity.xml(new JaxbBean("foo")));
+            assertThat(response.getMediaType(), equalTo(MediaType.APPLICATION_XML_TYPE));
+
+            final JaxbBean foo = response.readEntity(JaxbBean.class);
+            assertThat(foo.value, equalTo("foo"));
+            assertThat(cr.invoked(), equalTo(0));
+            assertThat(mr.invoked(), equalTo(1));
+            assertThat(umr.invoked(), equalTo(1));
+        }
+    }
+
+    @Provider
+    @Produces("application/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JAXBContextResolverApp extends JAXBContextResolver {
+    }
+
+    @Provider
+    @Produces("application/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class MarshallerResolverApp extends MarshallerResolver {
+    }
+
+    @Provider
+    @Produces("application/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class UnmarshallerResolverApp extends UnmarshallerResolver {
+    }
+
+    @Path("/")
+    @Consumes("application/xml")
+    @Produces("application/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JaxbBeanResourceApp extends JaxbBeanResource {
+    }
+
+    public static class JAXBContextAppTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private JAXBContextResolverApp crApp;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            crApp = new JAXBContextResolverApp();
+            return new ResourceConfig(JaxbBeanResourceApp.class).registerInstances(cr, crApp);
+        }
+
+        @Test
+        public void testJAXBContextApp() throws Exception {
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "application/xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(2, crApp.invoked());
+        }
+    }
+
+    public static class UnmarshallerAppTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private MarshallerResolver mr;
+        private UnmarshallerResolver umr;
+        private MarshallerResolverApp mrApp;
+        private UnmarshallerResolverApp umrApp;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            mr = new MarshallerResolver();
+            umr = new UnmarshallerResolver();
+            mrApp = new MarshallerResolverApp();
+            umrApp = new UnmarshallerResolverApp();
+            return new ResourceConfig(JaxbBeanResourceApp.class).registerInstances(cr, mr, umr, mrApp, umrApp);
+        }
+
+        @Test
+        public void testUnmarshallerApp() throws Exception {
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "application/xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, mr.invoked());
+            assertEquals(0, umr.invoked());
+            assertEquals(1, mrApp.invoked());
+            assertEquals(1, umrApp.invoked());
+
+            assertEquals("foo", target().request()
+                    .post(Entity.entity(new JaxbBean("foo"), "application/xml;charset=UTF-8"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, mr.invoked());
+            assertEquals(0, umr.invoked());
+            assertEquals(2, mrApp.invoked());
+            assertEquals(2, umrApp.invoked());
+        }
+    }
+
+    @Provider
+    @Produces("text/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JAXBContextResolverText extends JAXBContextResolver {
+    }
+
+    @Provider
+    @Produces("text/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class MarshallerResolverText extends MarshallerResolver {
+    }
+
+    @Provider
+    @Produces("text/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class UnmarshallerResolverText extends UnmarshallerResolver {
+    }
+
+    @Path("/")
+    @Consumes("text/xml")
+    @Produces("text/xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JaxbBeanResourceText extends JaxbBeanResource {
+    }
+
+    public static class JAXBContextTextTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private JAXBContextResolverText crText;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            crText = new JAXBContextResolverText();
+            return new ResourceConfig(JaxbBeanResourceText.class).registerInstances(cr, crText);
+        }
+
+        @Test
+        public void testJAXBContextText() throws Exception {
+            assertEquals("foo", target().request().post(Entity.entity(new JaxbBean("foo"), "text/xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(2, crText.invoked());
+        }
+    }
+
+    public static class UnmarshallerTextTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private MarshallerResolver mr;
+        private UnmarshallerResolver umr;
+        private MarshallerResolverText mrText;
+        private UnmarshallerResolverText umrText;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            mr = new MarshallerResolver();
+            umr = new UnmarshallerResolver();
+            mrText = new MarshallerResolverText();
+            umrText = new UnmarshallerResolverText();
+            return new ResourceConfig(JaxbBeanResourceText.class).registerInstances(cr, mr, umr, mrText, umrText);
+        }
+
+        @Test
+        public void testUnmarshallerText() throws Exception {
+            assertEquals("foo", target().request().post(Entity.entity(new JaxbBean("foo"), "text/xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, mr.invoked());
+            assertEquals(0, umr.invoked());
+            assertEquals(1, mrText.invoked());
+            assertEquals(1, umrText.invoked());
+        }
+    }
+
+    @Provider
+    @Produces("text/foo+xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class MarshallerResolverFoo extends MarshallerResolver {
+    }
+
+    @Provider
+    @Produces("text/foo+xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class UnmarshallerResolverFoo extends UnmarshallerResolver {
+    }
+
+    @Path("/")
+    @Consumes("text/foo+xml")
+    @Produces("text/foo+xml")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JaxbBeanResourceFoo extends JaxbBeanResource {
+    }
+
+    public static class UnmarshallerFooTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private MarshallerResolver mr;
+        private UnmarshallerResolver umr;
+        private MarshallerResolverFoo mrFoo;
+        private UnmarshallerResolverFoo umrFoo;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            mr = new MarshallerResolver();
+            umr = new UnmarshallerResolver();
+            mrFoo = new MarshallerResolverFoo();
+            umrFoo = new UnmarshallerResolverFoo();
+            return new ResourceConfig(JaxbBeanResourceFoo.class).registerInstances(cr, mr, umr, mrFoo, umrFoo);
+        }
+
+        @Test
+        public void testUnmarshallerFoo() throws Exception {
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "text/foo+xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, mr.invoked());
+            assertEquals(0, umr.invoked());
+            assertEquals(1, mrFoo.invoked());
+            assertEquals(1, umrFoo.invoked());
+
+            assertEquals("foo", target().request()
+                    .post(Entity.entity(new JaxbBean("foo"), "text/foo+xml;charset=UTF-8"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, mr.invoked());
+            assertEquals(0, umr.invoked());
+            assertEquals(2, mrFoo.invoked());
+            assertEquals(2, umrFoo.invoked());
+        }
+    }
+
+    @Path("/")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JaxbBeanResourceAll {
+
+        @POST
+        @Consumes("application/foo+xml")
+        @Produces("application/foo+xml")
+        public JaxbBean get(JaxbBean b) {
+            return b;
+        }
+
+        @POST
+        @Consumes("application/xml")
+        @Produces("application/xml")
+        public JaxbBean getApp(JaxbBean b) {
+            return b;
+        }
+
+        @POST
+        @Consumes("text/xml")
+        @Produces("text/xml")
+        public JaxbBean getText(JaxbBean b) {
+            return b;
+        }
+    }
+
+    public static class JAXBContextAllTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private JAXBContextResolverApp crApp;
+        private JAXBContextResolverText crText;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            crApp = new JAXBContextResolverApp();
+            crText = new JAXBContextResolverText();
+            return new ResourceConfig(JaxbBeanResourceAll.class).registerInstances(cr, crApp, crText);
+        }
+
+        @Test
+        public void testJAXBContextAll() throws Exception {
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "application/foo+xml"), JaxbBean.class).value);
+            assertEquals(2, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "application/xml"), JaxbBean.class).value);
+            assertEquals(2, cr.invoked());
+            assertEquals(2, crApp.invoked());
+            assertEquals(0, crText.invoked());
+
+            assertEquals("foo", target().request().post(Entity.entity(new JaxbBean("foo"), "text/xml"), JaxbBean.class).value);
+            assertEquals(2, cr.invoked());
+            assertEquals(2, crApp.invoked());
+            assertEquals(2, crText.invoked());
+        }
+    }
+
+    public static class UnmarshallerAllTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private JAXBContextResolverApp crApp;
+        private JAXBContextResolverText crText;
+        private MarshallerResolver mr;
+        private UnmarshallerResolver umr;
+        private MarshallerResolverApp mrApp;
+        private UnmarshallerResolverApp umrApp;
+        private MarshallerResolverText mrText;
+        private UnmarshallerResolverText umrText;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            crApp = new JAXBContextResolverApp();
+            crText = new JAXBContextResolverText();
+            mr = new MarshallerResolver();
+            umr = new UnmarshallerResolver();
+            mrApp = new MarshallerResolverApp();
+            umrApp = new UnmarshallerResolverApp();
+            mrText = new MarshallerResolverText();
+            umrText = new UnmarshallerResolverText();
+            return new ResourceConfig(JaxbBeanResourceAll.class).registerInstances(cr, crApp, crText, mr, umr,
+                    mrApp, umrApp, mrText, umrText);
+        }
+
+        @Test
+        public void testUnmarshallerAll() throws Exception {
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "application/foo+xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+            assertEquals(1, mr.invoked());
+            assertEquals(1, umr.invoked());
+            assertEquals(0, mrApp.invoked());
+            assertEquals(0, umrApp.invoked());
+            assertEquals(0, mrText.invoked());
+            assertEquals(0, umrText.invoked());
+
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new JaxbBean("foo"), "application/xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+            assertEquals(1, mr.invoked());
+            assertEquals(1, umr.invoked());
+            assertEquals(1, mrApp.invoked());
+            assertEquals(1, umrApp.invoked());
+            assertEquals(0, mrText.invoked());
+            assertEquals(0, umrText.invoked());
+
+            assertEquals("foo", target().request().post(Entity.entity(new JaxbBean("foo"), "text/xml"), JaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+            assertEquals(1, mr.invoked());
+            assertEquals(1, umr.invoked());
+            assertEquals(1, mrApp.invoked());
+            assertEquals(1, umrApp.invoked());
+            assertEquals(1, mrText.invoked());
+            assertEquals(1, umrText.invoked());
+        }
+    }
+
+    @XmlRootElement
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class OtherJaxbBean {
+
+        public String value;
+
+        public OtherJaxbBean() {
+        }
+
+        public OtherJaxbBean(String str) {
+            value = str;
+        }
+
+        public boolean equals(Object o) {
+            return o instanceof JaxbBean && ((JaxbBean) o).value.equals(value);
+        }
+
+        public String toString() {
+            return "JAXBClass: " + value;
+        }
+    }
+
+    @Path("/")
+    @Ignore("This class is not a test class & must be ignored by the Enclosed test runner.")
+    public static class JaxbBeanResourceAllOtherJaxbBean {
+
+        @POST
+        @Consumes("application/foo+xml")
+        @Produces("application/foo+xml")
+        public OtherJaxbBean get(OtherJaxbBean b) {
+            return b;
+        }
+
+        @POST
+        @Consumes("application/xml")
+        @Produces("application/xml")
+        public OtherJaxbBean getApp(OtherJaxbBean b) {
+            return b;
+        }
+
+        @POST
+        @Consumes("text/xml")
+        @Produces("text/xml")
+        public OtherJaxbBean getText(OtherJaxbBean b) {
+            return b;
+        }
+    }
+
+    public static class JAXBContextAllWithOtherJaxbBeanTest extends AbstractTypeTester {
+
+        private JAXBContextResolver cr;
+        private JAXBContextResolverApp crApp;
+        private JAXBContextResolverText crText;
+
+        @Override
+        protected Application configure() {
+            cr = new JAXBContextResolver();
+            crApp = new JAXBContextResolverApp();
+            crText = new JAXBContextResolverText();
+            return new ResourceConfig(JaxbBeanResourceAllOtherJaxbBean.class).registerInstances(cr, crApp, crText);
+        }
+
+        @Test
+        public void testJAXBContextAllWithOtherJaxbBean() throws Exception {
+            assertEquals("foo", target().request()
+                    .post(Entity.entity(new OtherJaxbBean("foo"), "application/foo+xml"), OtherJaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+
+            assertEquals("foo", target().request()
+                    .post(Entity.entity(new OtherJaxbBean("foo"), "application/xml"), OtherJaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+
+            assertEquals("foo",
+                    target().request().post(Entity.entity(new OtherJaxbBean("foo"), "text/xml"), OtherJaxbBean.class).value);
+            assertEquals(0, cr.invoked());
+            assertEquals(0, crApp.invoked());
+            assertEquals(0, crText.invoked());
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBean.java
new file mode 100644
index 0000000..6acd2df
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBean.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Doug Kohlert
+ */
+@XmlRootElement(name = "jaxbBean")
+public class JaxbBean {
+
+    public String value;
+
+    public JaxbBean() {
+    }
+
+    public JaxbBean(String str) {
+        value = str;
+    }
+
+    public boolean equals(Object o) {
+        if (!(o instanceof JaxbBean)) {
+            return false;
+        }
+        return ((JaxbBean) o).value.equals(value);
+    }
+
+    public String toString() {
+        return "JAXBClass: " + value;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBeanType.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBeanType.java
new file mode 100644
index 0000000..deab480
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBeanType.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ *
+ * @author Paul Sandoz
+ */
+@XmlType(name = "dada")
+public class JaxbBeanType {
+
+    public String value;
+
+    public JaxbBeanType() {
+    }
+
+    public JaxbBeanType(String str) {
+        value = str;
+    }
+
+    public boolean equals(Object o) {
+        if (!(o instanceof JaxbBeanType)) {
+            return false;
+        }
+        return ((JaxbBeanType) o).value.equals(value);
+    }
+
+    public String toString() {
+        return "JaxbBeanType: " + value;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JsonMoxyTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JsonMoxyTest.java
new file mode 100644
index 0000000..35a03a1
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/JsonMoxyTest.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@RunWith(ConcurrentRunner.class)
+public class JsonMoxyTest extends AbstractTypeTester {
+
+    @Path("JAXBElementListResource")
+    @Produces({"application/json"})
+    @Consumes({"application/json"})
+    public static class JAXBElementListResource extends AResource<List<JAXBElement<String>>> {
+    }
+
+    private List<JAXBElement<String>> getJAXBElementList() {
+        return Arrays.asList(getJAXBElementArray());
+    }
+
+    public static final class MoxyJsonConfigurationContextResolver implements ContextResolver<MoxyJsonConfig> {
+
+        @Override
+        public MoxyJsonConfig getContext(final Class<?> type) {
+            final MoxyJsonConfig configuration = new MoxyJsonConfig();
+            configuration.setIncludeRoot(true);
+            return configuration;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void _testListOrArray(final boolean isList, final MediaType mt) {
+        final Object in = isList ? getJAXBElementList() : getJAXBElementArray();
+        final GenericType gt = isList ? new GenericType<List<JAXBElement<String>>>() {
+        } : new GenericType<JAXBElement<String>[]>() {
+        };
+
+        final WebTarget target = target(isList ? "JAXBElementListResource" : "JAXBElementArrayResource");
+        final Object out = target.request(mt).post(Entity.entity(new GenericEntity(in, gt.getType()), mt), gt);
+
+        final List<JAXBElement<String>> inList =
+                isList ? ((List<JAXBElement<String>>) in) : Arrays.asList((JAXBElement<String>[]) in);
+        final List<JAXBElement<String>> outList = isList ? ((List<JAXBElement<String>>) out) : Arrays
+                .asList((JAXBElement<String>[]) out);
+        assertEquals("Lengths differ", inList.size(), outList.size());
+        for (int i = 0; i < inList.size(); i++) {
+            assertEquals("Names of elements at index " + i + " differ", inList.get(i).getName(), outList.get(i).getName());
+            assertEquals("Values of elements at index " + i + " differ", inList.get(i).getValue(), outList.get(i).getValue());
+        }
+    }
+
+    @Test
+    public void testJAXBElementListJSONRepresentation() {
+        _testListOrArray(true, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Path("JAXBElementArrayResource")
+    @Produces({"application/json"})
+    @Consumes({"application/json"})
+    public static class JAXBElementArrayResource extends AResource<JAXBElement<String>[]> {
+    }
+
+    @SuppressWarnings("unchecked")
+    private JAXBElement<String>[] getJAXBElementArray() {
+        return new JAXBElement[] {
+                new JAXBElement(QName.valueOf("element1"), String.class, "ahoj"),
+                new JAXBElement(QName.valueOf("element2"), String.class, "nazdar")
+        };
+    }
+
+    @Test
+    public void testJAXBElementArrayJSONRepresentation() {
+        _testListOrArray(false, MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return ((ResourceConfig) super.configure())
+                .register(MoxyJsonFeature.class)
+                .register(MoxyJsonConfigurationContextResolver.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        super.configureClient(config);
+        config.register(MoxyJsonFeature.class);
+        config.register(MoxyJsonConfigurationContextResolver.class);
+    }
+
+    @Path("JAXBElementBeanJSONResource")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class JAXBElementBeanJSONResource extends AResource<JAXBElement<String>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanJSONRepresentation() {
+        final WebTarget target = target("JAXBElementBeanJSONResource");
+
+        final GenericType<JAXBElement<String>> genericType = new GenericType<JAXBElement<String>>() {
+        };
+        final GenericEntity<JAXBElement<String>> jaxbElementGenericEntity = new GenericEntity<>(
+                new JAXBElement<>(new QName("test"), String.class, "CONTENT"), genericType.getType());
+
+        final Response rib = target.request().post(
+                Entity.entity(jaxbElementGenericEntity, "application/json"));
+
+        // TODO: the following would not be needed if i knew how to workaround JAXBElement<String>.class literal
+        final byte[] inBytes = getRequestEntity();
+        final byte[] outBytes = getEntityAsByteArray(rib);
+
+        assertEquals(new String(outBytes), inBytes.length, outBytes.length);
+        for (int i = 0; i < inBytes.length; i++) {
+            if (inBytes[i] != outBytes[i]) {
+                assertEquals("Index: " + i, inBytes[i], outBytes[i]);
+            }
+        }
+    }
+
+    @Path("JaxbBeanResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JaxbBeanResourceJSON extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationJSON() {
+        final WebTarget target = target("JaxbBeanResourceJSON");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JaxbBeanResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JaxbBeanResourceJSONMediaType extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationJSONMediaType() {
+        final WebTarget target = target("JaxbBeanResourceJSONMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/foo+json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBElementBeanResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JAXBElementBeanResourceJSON extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationJSON() {
+        final WebTarget target = target("JAXBElementBeanResourceJSON");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBElementBeanResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JAXBElementBeanResourceJSONMediaType extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationJSONMediaType() {
+        final WebTarget target = target("JAXBElementBeanResourceJSONMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/foo+json"), JaxbBean.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JAXBTypeResourceJSON {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Test
+    public void testJAXBTypeRepresentationJSON() {
+        final WebTarget target = target("JAXBTypeResourceJSON");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/json"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JAXBTypeResourceJSONMediaType {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Test
+    public void testJAXBTypeRepresentationJSONMediaType() {
+        final WebTarget target = target("JAXBTypeResourceJSONMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/foo+json"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBListResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBListResource {
+
+        @POST
+        public List<JaxbBean> post(final List<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("set")
+        public Set<JaxbBean> postSet(final Set<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("queue")
+        public Queue<JaxbBean> postQueue(final Queue<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("stack")
+        public Stack<JaxbBean> postStack(final Stack<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("custom")
+        public MyArrayList<JaxbBean> postCustom(final MyArrayList<JaxbBean> l) {
+            return l;
+        }
+
+        @GET
+        public Collection<JaxbBean> get() {
+            final ArrayList<JaxbBean> l = new ArrayList<>();
+            l.add(new JaxbBean("one"));
+            l.add(new JaxbBean("two"));
+            l.add(new JaxbBean("three"));
+            return l;
+        }
+
+        @POST
+        @Path("type")
+        public List<JaxbBean> postType(final Collection<JaxbBeanType> l) {
+            final List<JaxbBean> beans = new ArrayList<>();
+            for (final JaxbBeanType t : l) {
+                beans.add(new JaxbBean(t.value));
+            }
+            return beans;
+        }
+    }
+
+    @Path("JAXBListResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBListResourceMediaType extends JAXBListResource {
+    }
+
+    @Path("JAXBListResourceJSON")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class JAXBListResourceJSON extends JAXBListResource {
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSONCollection() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSON");
+
+        final Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        Collection<JaxbBean> b = target.request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/json"), new GenericType<Collection<JaxbBean>>() {
+        });
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/json"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSONLinkedList() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSON");
+
+        Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        final Collection<JaxbBean> b;
+
+        a = new LinkedList<>(a);
+        b = target.path("queue").request().post(Entity.entity(new GenericEntity<Queue<JaxbBean>>((Queue<JaxbBean>) a) {
+        }, "application/json"), new GenericType<Queue<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSONSet() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSON");
+
+        Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        final Collection<JaxbBean> b;
+
+        a = new HashSet<>(a);
+        b = target.path("set").request().post(Entity.entity(new GenericEntity<Set<JaxbBean>>((Set<JaxbBean>) a) {
+        }, "application/json"), new GenericType<Set<JaxbBean>>() {
+        });
+        final Comparator<JaxbBean> c = new Comparator<JaxbBean>() {
+            @Override
+            public int compare(final JaxbBean t, final JaxbBean t1) {
+                return t.value.compareTo(t1.value);
+            }
+        };
+        final TreeSet<JaxbBean> t1 = new TreeSet<>(c);
+        final TreeSet<JaxbBean> t2 = new TreeSet<>(c);
+        t1.addAll(a);
+        t2.addAll(b);
+        assertEquals(t1, t2);
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSONStack() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSON");
+
+        final Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        final Collection<JaxbBean> b;
+
+        final Stack<JaxbBean> s = new Stack<>();
+        s.addAll(a);
+        b = target.path("stack").request().post(Entity.entity(new GenericEntity<Stack<JaxbBean>>(s) {
+        }, "application/json"), new GenericType<Stack<JaxbBean>>() {
+        });
+        assertEquals(s, b);
+    }
+
+    @Test
+    @Ignore("Until JERSEY-2825 is fixed.")
+    public void testJAXBListRepresentationJSONArrayList() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSON");
+
+        Collection<JaxbBean> a = target.request().get(new GenericType<Collection<JaxbBean>>() {});
+        final Collection<JaxbBean> b;
+
+        a = new MyArrayList<>(a);
+        b = target.path("custom").request()
+                .post(Entity.entity(new GenericEntity<MyArrayList<JaxbBean>>((MyArrayList<JaxbBean>) a) {}, "application/json"),
+                        new GenericType<MyArrayList<JaxbBean>>() {});
+        assertEquals(a, b);
+    }
+
+    @Path("JAXBListResourceJSONMediaType")
+    @Produces("application/foo+json")
+    @Consumes("application/foo+json")
+    public static class JAXBListResourceJSONMediaType extends JAXBListResource {
+    }
+
+    @Test
+    public void testJAXBListRepresentationJSONMediaType() throws Exception {
+        final WebTarget target = target("JAXBListResourceJSONMediaType");
+
+        final Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        Collection<JaxbBean> b = target.request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/foo+json"), new GenericType<Collection<JaxbBean>>() {
+        });
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/foo+json"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MediaTypeSelectionTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MediaTypeSelectionTest.java
new file mode 100644
index 0000000..77f7df7
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MediaTypeSelectionTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class MediaTypeSelectionTest extends AbstractTypeTester {
+    @Path("form")
+    public static class FormResource {
+        @POST
+        public Response post(MultivaluedMap<String, String> data) {
+            return Response.ok(data, MediaType.APPLICATION_FORM_URLENCODED_TYPE).build();
+        }
+    }
+
+    @Path("foo")
+    public static class FooResource {
+        @POST
+        @Consumes("foo/*")
+        @Produces("foo/*")
+        public String foo(String foo) {
+            return foo;
+        }
+    }
+
+    @Path("text")
+    public static class TextResource {
+        @GET
+        @Produces("text/*")
+        public String getText() {
+            return "text";
+        }
+
+        @GET
+        @Produces("application/*")
+        @Path("any")
+        public String getAny() {
+            return "text";
+        }
+
+        @POST
+        @Produces("text/*")
+        public Response post(String entity) {
+            return Response.ok().entity("entity").build();
+        }
+    }
+
+    @Path("wildcard")
+    public static class WildCardResource {
+        @POST
+        public String wildCard(String wc) {
+            return wc;
+        }
+    }
+
+    @Path("jira/1518")
+    public static class Issue1518Resource {
+        @POST
+        @Consumes("text/plain;qs=0.7")
+        public String never() {
+            throw new WebApplicationException(Response.Status.CONFLICT);
+        }
+
+        @POST
+        @Consumes("text/*")
+        public String text() {
+            return "1518";
+        }
+    }
+
+    // JERSEY-1518 reproducer test
+    @Test
+    public void testQsInConsumes() {
+        Response r = target("jira/1518").request(MediaType.TEXT_PLAIN_TYPE).post(Entity.text("request"));
+        assertEquals(200, r.getStatus());
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, r.getMediaType());
+        assertEquals("1518", r.readEntity(String.class));
+    }
+
+    // JERSEY-1187 regression test
+    @Test
+    public void testExplicitMediaType() {
+        Response r = target("form").request().post(Entity.form(new Form().param("a", "b")));
+        assertEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, r.getMediaType());
+        assertEquals("b", r.readEntity(Form.class).asMap().getFirst("a"));
+    }
+
+    @Test
+    public void testAmbiguousWildcard() {
+        Response r = target("foo").request().post(Entity.entity("test", "foo/plain"));
+        assertEquals(406, r.getStatus());
+    }
+
+    @Test
+    public void testWildcardInSubType() {
+        Response r = target("text").request("text/*").get();
+        assertEquals(406, r.getStatus());
+    }
+
+    @Test
+    public void testWildcardInSubTypePost() {
+        Response r = target("text").request("text/*").post(Entity.entity("test", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(406, r.getStatus());
+    }
+
+    @Test
+    public void testWildcardInSubType2() {
+        Response r = target("text").request("*/*").get();
+        assertEquals(406, r.getStatus());
+    }
+
+    @Test
+    public void testWildcardsInTypeAndSubType() {
+        Response r = target("text/any").request("*/*").get();
+        assertEquals(200, r.getStatus());
+        assertEquals(MediaType.APPLICATION_OCTET_STREAM_TYPE, r.getMediaType());
+    }
+
+    @Test
+    public void testNoAcceptHeader() {
+        // This test is testing the situation when the client sends no Accept header to the server and it expects
+        // APPLICATION_OCTET_STREAM_TYPE to be returned. But when no Accept header is defined by the client api the
+        // HttpURLConnection (in HttpUrlConnector)  always put there some default Accept header (like */*, text/plain, ...).
+        // To overwrite this behaviour we set Accept to empty String. This works fine as the server code handles empty
+        // Accept header like no Accept header.
+        final MultivaluedHashMap headers = new MultivaluedHashMap();
+        headers.add("Accept", "");
+
+        Response r = target("text/any").request().headers(headers).get();
+        assertEquals(200, r.getStatus());
+        assertEquals(MediaType.APPLICATION_OCTET_STREAM_TYPE, r.getMediaType());
+    }
+
+    @Test
+    public void testSpecific() {
+        Response r = target("foo").request("foo/plain").post(Entity.entity("test", "foo/plain"));
+        assertEquals(MediaType.valueOf("foo/plain"), r.getMediaType());
+        assertEquals("test", r.readEntity(String.class));
+    }
+
+    @Test
+    @Ignore("JSONB breaks this test.")
+    public void testApplicationWildCard() {
+        Response r = target("wildcard").request("application/*").post(Entity.text("test"));
+        assertEquals(MediaType.APPLICATION_OCTET_STREAM_TYPE, r.getMediaType());
+        assertEquals("test", r.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MessageBodyProviderAnnotationsTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MessageBodyProviderAnnotationsTest.java
new file mode 100644
index 0000000..897bfe4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MessageBodyProviderAnnotationsTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests annotations passed to message body provider.
+ *
+ * @author Miroslav Fuksa
+ */
+public class MessageBodyProviderAnnotationsTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ContainerReaderWriter.class, Resource.class);
+    }
+
+    @Test
+    public void testWriter() {
+        String get = target().path("test").request("test/text").get(String.class);
+        assertEquals("get-ok", get);
+    }
+
+    @Test
+    public void testReader() {
+        String get = target().path("test").request("text/plain").post(Entity.entity("test", "test/text"), String.class);
+        assertEquals("ok", get);
+    }
+
+
+    @Produces("test/text")
+    @Consumes("test/text")
+    public static class ContainerReaderWriter implements MessageBodyWriter<String>, MessageBodyReader<Bean> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                WebApplicationException {
+            OutputStreamWriter osw = new OutputStreamWriter(entityStream);
+
+            if (compareAnnotations(new Class<?>[]{MyAnnotation.class, Produces.class, GET.class}, annotations)) {
+                osw.write(s + "-ok");
+            } else {
+                osw.write(s + "-fail");
+            }
+            osw.flush();
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == Bean.class;
+        }
+
+        @Override
+        public Bean readFrom(Class<Bean> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                             MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException,
+                WebApplicationException {
+            if (compareAnnotations(new Class<?>[]{MyAnnotation.class}, annotations)) {
+                return new Bean("ok");
+            } else {
+                return new Bean("fail");
+            }
+        }
+    }
+
+    public static class Bean {
+        private final String value;
+
+        public Bean(String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+    private static boolean compareAnnotations(Class[] expected, Annotation[] actual) {
+        if (expected.length != actual.length) {
+            return false;
+        }
+
+        for (Class<?> e : expected) {
+            boolean found = false;
+            for (Annotation a : actual) {
+                if (e.equals(a.annotationType())) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static final Annotation ANNOTATION;
+
+    @Retention(RetentionPolicy.RUNTIME)
+    public static @interface MyAnnotation {
+    }
+
+    static {
+        @MyAnnotation
+        class TempClass {
+        }
+        ;
+        ANNOTATION = TempClass.class.getAnnotation(MyAnnotation.class);
+    }
+
+    @Path("test")
+    public static class Resource {
+        @GET
+        @Produces("test/text")
+        public Response get() {
+            Response response = Response.ok().entity("get", new Annotation[]{ANNOTATION}).build();
+            return response;
+        }
+
+        @POST
+        @Consumes("test/text")
+        @Produces("text/plain")
+        public String post(@MyAnnotation Bean entity) {
+            return entity.getValue();
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MultipartTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MultipartTest.java
new file mode 100644
index 0000000..449da72
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MultipartTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Martin Matula
+ */
+public class MultipartTest extends JerseyTest {
+
+    @SuppressWarnings("UnusedDeclaration")
+    public static class MyObject {
+
+        private String value;
+
+        public MyObject() {
+        }
+
+        public MyObject(final String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(final String value) {
+            this.value = value;
+        }
+    }
+
+    @Path("/")
+    public static class MultipartResource {
+
+        @POST
+        @Path("filename")
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        @Produces(MediaType.TEXT_PLAIN)
+        public String filename(final FormDataMultiPart entity) {
+            return entity.getField("text").getValue() + entity.getField("file").getContentDisposition().getFileName();
+        }
+
+        @POST
+        @Path("mbr")
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        @Produces(MediaType.TEXT_PLAIN)
+        public Response mbr(final FormDataMultiPart entity) {
+            entity.getField("text").getValueAs(MultipartResource.class);
+
+            return Response.ok("ko").build();
+        }
+
+        @POST
+        @Path("listAsParameter")
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        public String process(@FormDataParam(value = "object") final MyObject object,
+                              @FormDataParam(value = "list") final List<MyObject> list) {
+            String value = object.value;
+
+            for (final MyObject obj : list) {
+                value += "_" + obj.value;
+            }
+
+            return value;
+        }
+    }
+
+    public static class MessageBodyProvider
+            implements MessageBodyReader<MultipartResource>, MessageBodyWriter<MultipartResource> {
+
+        @Override
+        public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                  final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public MultipartResource readFrom(final Class<MultipartResource> type, final Type genericType,
+                                          final Annotation[] annotations, final MediaType mediaType,
+                                          final MultivaluedMap<String, String> httpHeaders,
+                                          final InputStream entityStream) throws IOException, WebApplicationException {
+            throw new IOException("IOE");
+        }
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(final MultipartResource multipartResource, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final MultipartResource multipartResource, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
+                throws IOException, WebApplicationException {
+
+            throw new IOException("IOE");
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MultipartResource.class, MessageBodyProvider.class)
+                .register(MultiPartFeature.class)
+                .register(JacksonFeature.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(MultiPartFeature.class);
+        config.register(JacksonFeature.class);
+    }
+
+    @Test
+    public void testFileNameInternetExplorer() throws Exception {
+        final InputStream entity = getClass().getResourceAsStream("multipart-testcase.txt");
+        final Response response = target("filename")
+                .request()
+                .header("User-Agent", "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)")
+                .post(Entity.entity(entity, "multipart/form-data; boundary=---------------------------7dc941520888"));
+
+        assertThat(response.readEntity(String.class), equalTo("bhhklbpom.xml"));
+    }
+
+    @Test
+    public void testFileName() throws Exception {
+        final InputStream entity = getClass().getResourceAsStream("multipart-testcase.txt");
+        final Response response = target("filename")
+                .request()
+                .post(Entity.entity(entity, "multipart/form-data; boundary=---------------------------7dc941520888"));
+
+        assertThat(response.readEntity(String.class), equalTo("bhhklbC:javaprojectsmultipart-testcasepom.xml"));
+    }
+
+    @Test
+    public void testMbrExceptionServer() throws Exception {
+        final InputStream entity = getClass().getResourceAsStream("multipart-testcase.txt");
+        final Response response = target("mbr")
+                .request()
+                .post(Entity.entity(entity, "multipart/form-data; boundary=---------------------------7dc941520888"));
+
+        assertThat(response.getStatus(), equalTo(500));
+    }
+
+    /**
+     * Test that injection of a list (specific type) works.
+     */
+    @Test
+    public void testSpecificListAsParameter() throws Exception {
+        final MyObject object = new MyObject("object");
+        final List<MyObject> list = Arrays.asList(new MyObject("list1"), new MyObject("list2"));
+
+        final FormDataMultiPart mp = new FormDataMultiPart();
+        mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("object").fileName("object").build(),
+                object, MediaType.APPLICATION_JSON_TYPE));
+        mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("list").fileName("list").build(),
+                list, MediaType.APPLICATION_JSON_TYPE));
+
+        final Response response = target("listAsParameter")
+                .request().post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE));
+
+        assertThat(response.readEntity(String.class), is("object_list1_list2"));
+    }
+
+    @Test
+    public void testEmptyEntity() throws Exception {
+        final Response response = target("filename")
+                .request()
+                .post(Entity.entity(null, MediaType.MULTIPART_FORM_DATA_TYPE));
+
+        assertThat(response.getStatus(), is(400));
+    }
+
+    @Test
+    public void testEmptyEntityWithoutContentType() throws Exception {
+        final Response response = target("filename")
+                .request()
+                .post(null);
+
+        assertThat(response.getStatus(), is(400));
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MyArrayList.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MyArrayList.java
new file mode 100644
index 0000000..1919214
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/MyArrayList.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Martin Matula
+ */
+public class MyArrayList<T> extends ArrayList<T> {
+
+    public MyArrayList() {
+
+    }
+
+    public MyArrayList(final Collection<T> a) {
+        super(a);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/NoMessageBodyWorkerTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/NoMessageBodyWorkerTest.java
new file mode 100644
index 0000000..454fac3
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/NoMessageBodyWorkerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010, 2018 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.entity;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ */
+public class NoMessageBodyWorkerTest extends AbstractTypeTester {
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = (ResourceConfig) super.configure();
+        resourceConfig.property(CommonProperties.MOXY_JSON_FEATURE_DISABLE, true);
+        return resourceConfig;
+    }
+
+    @Path("nobodyreader")
+    public static class NoMessageBodyReaderResource {
+
+        @POST
+        public void post(NoMessageBodyReaderResource t) {
+        }
+    }
+
+    @Test
+    public void testNoMessageBodyReaderResource() {
+        Response r = target("nobodyreader").request().post(Entity.text("a"));
+        assertEquals(415, r.getStatus());
+    }
+
+    @Path("nobodywriter")
+    public static class NoMessageBodyWriterResource {
+
+        @GET
+        public NoMessageBodyWriterResource get() {
+            return new NoMessageBodyWriterResource();
+        }
+    }
+
+    @Test
+    public void testNoMessageBodyWriterResource() {
+        Response r = target("nobodywriter").request().get();
+        assertEquals(500, r.getStatus());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentOrderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentOrderTest.java
new file mode 100644
index 0000000..ef7bba6
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentOrderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the correct order of providers.
+ *
+ * @author Paul Sandoz
+ */
+public class ParameterTypeArgumentOrderTest extends AbstractParameterTypeArgumentOrderTest {
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(AWriter.class, BWriter.class, CWriter.class, ClassResource.class);
+    }
+
+    @Test
+    public void testClassResource() {
+        assertEquals("AA", target().path("a").request().get(String.class));
+        assertEquals("BB", target().path("b").request().get(String.class));
+        assertEquals("CC", target().path("c").request().get(String.class));
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentResourceReaderWriterOrderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentResourceReaderWriterOrderTest.java
new file mode 100644
index 0000000..58aa798
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentResourceReaderWriterOrderTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the correct order of providers.
+ *
+ * @author Paul Sandoz
+ */
+public class ParameterTypeArgumentResourceReaderWriterOrderTest extends AbstractParameterTypeArgumentOrderTest {
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(AReaderWriter.class, BReaderWriter.class, CReaderWriter.class, ClassResource.class);
+    }
+
+    @Test
+    public void testClassResource() {
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        assertEquals("AA", target().path("a").request("text/html").get(String.class));
+        assertEquals("BB", target().path("b").request("text/html").get(String.class));
+        assertEquals("CC", target().path("c").request("text/html").get(String.class));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentReversedOrderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentReversedOrderTest.java
new file mode 100644
index 0000000..8fc826b
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentReversedOrderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the correct order of providers.
+ *
+ * @author Paul Sandoz
+ */
+public class ParameterTypeArgumentReversedOrderTest extends AbstractParameterTypeArgumentOrderTest {
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(CWriter.class, BWriter.class, AWriter.class, ClassResource.class);
+    }
+
+    @Test
+    public void testClassResource() {
+        assertEquals("AA", target().path("a").request().get(String.class));
+        assertEquals("BB", target().path("b").request().get(String.class));
+        assertEquals("CC", target().path("c").request().get(String.class));
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/RenderedImageTypeTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/RenderedImageTypeTest.java
new file mode 100644
index 0000000..1d379e0
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/RenderedImageTypeTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.InputStream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class RenderedImageTypeTest extends JerseyTest {
+
+    @Path("/")
+    public static class ImageResource {
+
+        @Consumes("image/gif")
+        @Produces("image/png")
+        @POST
+        public RenderedImage postGif(final RenderedImage image) {
+            return image;
+        }
+
+        @Consumes("image/png")
+        @Produces("image/png")
+        @POST
+        public RenderedImage postPng(final RenderedImage image) {
+            return image;
+        }
+
+        @Path("sub")
+        @Consumes("application/octet-stream")
+        @Produces("image/png")
+        @POST
+        public RenderedImage postUndefined(final BufferedImage image) {
+            return image;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ImageResource.class);
+    }
+
+    @Test
+    public void testPostPng() throws Exception {
+        final InputStream stream = getClass().getResourceAsStream("Jersey_yellow.png");
+        Response response = target().request().post(Entity.entity(stream, "image/png"));
+        assertThat(Long.valueOf(response.getHeaderString("Content-Length")), greaterThan(0L));
+
+        final RenderedImage image = response.readEntity(RenderedImage.class);
+        assertThat(image, notNullValue());
+
+        response = target().request().post(Entity.entity(image, "image/png"));
+        assertThat(response.readEntity(RenderedImage.class), notNullValue());
+        assertThat(Long.valueOf(response.getHeaderString("Content-Length")), greaterThan(0L));
+    }
+
+    @Test
+    public void testPostGif() throws Exception {
+        final InputStream stream = getClass().getResourceAsStream("duke_rocket.gif");
+        Response response = target().request().post(Entity.entity(stream, "image/gif"));
+        assertThat(Long.valueOf(response.getHeaderString("Content-Length")), greaterThan(0L));
+
+        final RenderedImage image = response.readEntity(RenderedImage.class);
+        assertThat(image, notNullValue());
+
+        response = target().request().post(Entity.entity(image, "image/png"));
+        assertThat(response.readEntity(RenderedImage.class), notNullValue());
+        assertThat(Long.valueOf(response.getHeaderString("Content-Length")), greaterThan(0L));
+    }
+
+    @Test
+    public void testPostUndefined() throws Exception {
+        final InputStream stream = getClass().getResourceAsStream("duke_rocket.gif");
+        Response response = target("sub").request().post(Entity.entity(stream, "application/octet-stream"));
+        assertThat(Long.valueOf(response.getHeaderString("Content-Length")), greaterThan(0L));
+
+        final RenderedImage image = response.readEntity(RenderedImage.class);
+        assertThat(image, notNullValue());
+
+        response = target().request().post(Entity.entity(image, "image/png"));
+        assertThat(response.readEntity(RenderedImage.class), notNullValue());
+        assertThat(Long.valueOf(response.getHeaderString("Content-Length")), greaterThan(0L));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/StreamingOutputTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/StreamingOutputTest.java
new file mode 100644
index 0000000..9bdf547
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/StreamingOutputTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+@Path("/")
+public class StreamingOutputTest extends JerseyTest {
+    @GET
+    @Path("wae")
+    public StreamingOutput test2() {
+        return new StreamingOutput() {
+            public void write(OutputStream output) throws IOException {
+                throw new WebApplicationException(Response.Status.BAD_GATEWAY.getStatusCode());
+            }
+        };
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(StreamingOutputTest.class);
+    }
+
+    @Test
+    public void testWebApplicationException() {
+        Response r = target("wae").request().get();
+        assertEquals(Response.Status.BAD_GATEWAY.getStatusCode(), r.getStatusInfo().getStatusCode());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/SubResourceDynamicProxyTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/SubResourceDynamicProxyTest.java
new file mode 100644
index 0000000..eab22f4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/SubResourceDynamicProxyTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer for JERSEY-2541.
+ *
+ * Make sure that type parameter will be retained for resource method
+ * of a sub-resource locator that returns a dynamic proxy.
+ * This test should cover also the EJB case, as the common cause
+ * is missing type parameter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class SubResourceDynamicProxyTest extends JerseyTest {
+
+    @Override
+    public Application configure() {
+        return new ResourceConfig(ListProvider.class, RootResource.class);
+    }
+
+    /**
+     * Sub-resource interface so that it is easy to make a dynamic proxy
+     * with the standard Java reflection API.
+     */
+    public static interface SubResource {
+
+        @GET
+        public List<Foo> getGreeting();
+    }
+
+    /**
+     * Helper type to be used as a type parameter.
+     */
+    public static class Foo {
+    }
+
+    /**
+     * Message body writer that uses to check presence of type parameter in the provided entity type.
+     * If no type parameter is found the provider refuses to process corresponding entity,
+     * which would lead to an error.
+     */
+    @Provider
+    public static class ListProvider implements MessageBodyWriter<List<Foo>> {
+
+        /**
+         * This is the data that would be written to the response body by the {@link ListProvider} bellow.
+         */
+        static final String CHECK_DATA = "ensure this one makes it to the client";
+
+        /**
+         * We need to work with a non-null entity here so that the worker could do it's job.
+         */
+        static final LinkedList<Foo> TEST_ENTITY = new LinkedList<Foo>() {
+            {
+                add(new Foo());
+            }
+        };
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            if (!(genericType instanceof ParameterizedType)) {
+                return false;
+            }
+
+            final ParameterizedType pt = (ParameterizedType) genericType;
+
+            if (pt.getActualTypeArguments().length > 1) {
+                return false;
+            }
+
+            if (!(pt.getActualTypeArguments()[0] instanceof Class)) {
+                return false;
+            }
+
+            final Class listClass = (Class) pt.getActualTypeArguments()[0];
+            return listClass == Foo.class;
+        }
+
+        @Override
+        public long getSize(List<Foo> t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(List<Foo> t, Class<?> type, Type genericType,
+                            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+
+            assertEquals(t, TEST_ENTITY);
+            entityStream.write(CHECK_DATA.getBytes());
+        }
+    }
+
+    @Path("root")
+    public static class RootResource {
+
+        /**
+         * Sub-resource locator is used here, so that resource model will be built
+         * at runtime, when the actual handler is the dynamic proxy.
+         *
+         * @return dynamic proxy for the sub-resource
+         */
+        @Path("sub")
+        public SubResource getSubresource() {
+            return (SubResource) Proxy.newProxyInstance(
+                    RootResource.class.getClassLoader(),
+                    new Class<?>[] {SubResource.class},
+                    new InvocationHandler() {
+
+                        @Override
+                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                            return ListProvider.TEST_ENTITY;
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Make sure the request is processed without errors, and the data
+     * written by the {@link ListProvider} is returned back to the client.
+     */
+    @Test
+    public void testSubResourceProxy() {
+
+        final Response response = target("/root/sub").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals(ListProvider.CHECK_DATA, response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XXETest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XXETest.java
new file mode 100644
index 0000000..8e3d3b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XXETest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity;
+
+import java.net.URL;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests xml security.
+ *
+ * @author Paul Sandoz
+ */
+public class XXETest extends JerseyTest {
+
+    private static final String DOCTYPE = "<!DOCTYPE foo [<!ENTITY xxe SYSTEM \"%s\">]>";
+    private static final String XML = "<jaxbBean><value>&xxe;</value></jaxbBean>";
+
+    private String getDocument() {
+        final URL u = this.getClass().getResource("xxe.txt");
+        return String.format(DOCTYPE, u.toString()) + XML;
+    }
+
+    private String getListDocument() {
+        final URL u = this.getClass().getResource("xxe.txt");
+        return String.format(DOCTYPE, u.toString()) + "<jaxbBeans>" + XML + XML + XML + "</jaxbBeans>";
+    }
+
+    @Path("/")
+    @Consumes("application/xml")
+    @Produces("application/xml")
+    public static class EntityHolderResource {
+
+        @Path("jaxb")
+        @POST
+        public String post(final JaxbBean s) {
+            return s.value;
+        }
+
+        @Path("jaxbelement")
+        @POST
+        public String post(final JAXBElement<JaxbBeanType> s) {
+            return s.getValue().value;
+        }
+
+        @Path("jaxb/list")
+        @POST
+        public String post(final List<JaxbBean> s) {
+            return s.get(0).value;
+        }
+
+        @Path("sax")
+        @POST
+        public SAXSource postSax(final SAXSource s) {
+            return s;
+        }
+
+        @Path("dom")
+        @POST
+        public String postDom(final DOMSource s) {
+            final Document d = (Document) s.getNode();
+            final Element e = (Element) d.getElementsByTagName("value").item(0);
+            final Node n = e.getChildNodes().item(0);
+            if (n.getNodeType() == Node.TEXT_NODE) {
+                return n.getNodeValue();
+            } else if (n.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
+                return "";
+            } else {
+                throw new WebApplicationException(400);
+            }
+        }
+
+        @Path("stream")
+        @POST
+        public StreamSource postStream(final StreamSource s) {
+            return s;
+        }
+    }
+
+    @Override
+    public ResourceConfig configure() {
+        return new ResourceConfig(EntityHolderResource.class);
+    }
+
+    @Test
+    public void testJAXBSecure() {
+        final String s = target().path("jaxb").request("application/xml").post(Entity.entity(getDocument(),
+                MediaType.APPLICATION_XML_TYPE), String.class);
+        assertEquals("", s);
+    }
+
+    @Test
+    public void testJAXBSecureWithThreads() throws Throwable {
+        final int n = 4;
+        final CountDownLatch latch = new CountDownLatch(n);
+
+        final Runnable runnable = new Runnable() {
+            public void run() {
+                try {
+                    final String s = target().path("jaxb").request("application/xml").post(Entity.entity(getDocument(),
+                            MediaType.TEXT_PLAIN_TYPE), String.class);
+                    assertEquals("", s);
+                } finally {
+                    latch.countDown();
+                }
+            }
+        };
+
+        final Set<Throwable> s = new HashSet<Throwable>();
+        for (int i = 0; i < n; i++) {
+            final Thread t = new Thread(runnable);
+            t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+                public void uncaughtException(final Thread t, final Throwable ex) {
+                    s.add(ex);
+                }
+            });
+            t.start();
+        }
+
+        try {
+            latch.await();
+        } catch (final InterruptedException ignored) {
+        }
+    }
+
+    @Test
+    public void testJAXBElementSecure() {
+        final String s = target().path("jaxbelement").request("application/xml").post(Entity.entity(getDocument(),
+                MediaType.APPLICATION_XML_TYPE), String.class);
+        assertEquals("", s);
+    }
+
+    @Ignore // TODO
+    @Test
+    public void testJAXBListSecure() {
+        final String s = target().path("jaxb/list").request("application/xml").post(Entity.entity(getListDocument(),
+                MediaType.APPLICATION_XML_TYPE), String.class);
+        assertEquals("", s);
+    }
+
+    @Test
+    public void testSAXSecure() {
+        final JaxbBean b = target().path("sax").request("application/xml").post(Entity.entity(getDocument(),
+                MediaType.APPLICATION_XML_TYPE), JaxbBean.class);
+        assertEquals("", b.value);
+    }
+
+    @Test
+    public void testDOMSecure() {
+        final String s = target().path("dom").request("application/xml").post(Entity.entity(getDocument(),
+                MediaType.APPLICATION_XML_TYPE), String.class);
+        assertEquals("", s);
+    }
+
+    @Test
+    public void testStreamSecure() {
+        final JaxbBean b = target().path("stream").request("application/xml").post(Entity.entity(getDocument(),
+                MediaType.APPLICATION_XML_TYPE), JaxbBean.class);
+        assertEquals("", b.value);
+    }
+
+    // NOTE - this is a tes migrated from Jersey 1.x tests. The original test class contains also insecure "versions" of the
+    // methods above configured via FeaturesAndProperties.FEATURE_DISABLE_XML_SECURITY. Those methods are ommited,
+    // as Jersey 2 does not support such consturct.
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlJaxBElementProviderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlJaxBElementProviderTest.java
new file mode 100644
index 0000000..beb84eb
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlJaxBElementProviderTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Miroslav Fuksa
+ *
+ */
+public class XmlJaxBElementProviderTest extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Atom.class);
+    }
+
+    @Path("atom")
+    public static class Atom {
+        @Context
+        HttpHeaders headers;
+
+        @Path("wildcard")
+        @POST
+        @Consumes("application/*")
+        @Produces("application/*")
+        public Response wildcard(JAXBElement<String> jaxb) {
+            MediaType media = headers.getMediaType();
+            return Response.ok(jaxb).type(media).build();
+        }
+
+        @Path("atom")
+        @POST
+        @Consumes("application/atom+xml")
+        @Produces("application/atom+xml")
+        public Response atom(JAXBElement<String> jaxb) {
+            MediaType media = headers.getMediaType();
+            return Response.ok(jaxb).type(media).build();
+        }
+
+        @Path("empty")
+        @POST
+        public Response emptyConsumesProduces(JAXBElement<String> jaxb) {
+            MediaType media = headers.getMediaType();
+            return Response.ok(jaxb).type(media).build();
+        }
+    }
+
+    @Test
+    public void testWildcard() {
+        final String path = "atom/wildcard";
+        _test(path);
+    }
+
+    private void _test(String path) {
+        WebTarget target = target(path);
+        final Response res = target.request("application/atom+xml").post(
+                Entity.entity(new JAXBElement<String>(new QName("atom"), String.class, "value"),
+                        "application/atom+xml"));
+        assertEquals(200, res.getStatus());
+        final GenericType<JAXBElement<String>> genericType = new GenericType<JAXBElement<String>>() {};
+        final JAXBElement<String> stringJAXBElement = res.readEntity(genericType);
+        assertEquals("value", stringJAXBElement.getValue());
+    }
+
+    @Test
+    public void testAtom() {
+        final String path = "atom/atom";
+        _test(path);
+    }
+
+    @Test
+    public void testEmpty() {
+        final String path = "atom/empty";
+        _test(path);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlMoxyTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlMoxyTest.java
new file mode 100644
index 0000000..2e95ca1
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlMoxyTest.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 2012, 2018 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.entity;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.namespace.QName;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import org.junit.runner.RunWith;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@RunWith(ConcurrentRunner.class)
+public class XmlMoxyTest extends AbstractTypeTester {
+
+    @Path("JaxbBeanResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JaxbBeanResource extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentation() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanResource.class, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Path("JaxbBeanResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JaxbBeanResourceMediaType extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationMediaType() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanResourceMediaType.class, MediaType.valueOf("application/foo+xml"));
+    }
+
+    @Test
+    public void testJaxbBeanRepresentationError() {
+        final WebTarget target = target("JaxbBeanResource");
+
+        final String xml = "<root>foo</root>";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("JaxbBeanTextResource")
+    @Produces("text/xml")
+    @Consumes("text/xml")
+    public static class JaxbBeanTextResource extends AResource<JaxbBean> {
+    }
+
+    @Test
+    public void testJaxbBeanTextRepresentation() {
+        _test(new JaxbBean("CONTENT"), JaxbBeanTextResource.class, MediaType.TEXT_XML_TYPE);
+    }
+
+    @Path("JAXBElementBeanResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBElementBeanResource extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentation() {
+        _test(new JaxbBean("CONTENT"), JAXBElementBeanResource.class, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Path("JAXBElementListResource")
+    @Produces({"application/xml", "application/json"})
+    @Consumes({"application/xml", "application/json"})
+    public static class JAXBElementListResource extends AResource<List<JAXBElement<String>>> {
+    }
+
+    private List<JAXBElement<String>> getJAXBElementList() {
+        return Arrays.asList(getJAXBElementArray());
+    }
+
+    @Test
+    public void testJAXBElementListXMLRepresentation() {
+        _testListOrArray(true, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void _testListOrArray(final boolean isList, final MediaType mt) {
+        final Object in = isList ? getJAXBElementList() : getJAXBElementArray();
+        final GenericType gt = isList ? new GenericType<List<JAXBElement<String>>>() {
+        } : new GenericType<JAXBElement<String>[]>() {
+        };
+
+        final WebTarget target = target(isList ? "JAXBElementListResource" : "JAXBElementArrayResource");
+        final Object out = target.request(mt).post(Entity.entity(new GenericEntity(in, gt.getType()), mt), gt);
+
+        final List<JAXBElement<String>> inList =
+                isList ? ((List<JAXBElement<String>>) in) : Arrays.asList((JAXBElement<String>[]) in);
+        final List<JAXBElement<String>> outList = isList ? ((List<JAXBElement<String>>) out) : Arrays
+                .asList((JAXBElement<String>[]) out);
+        assertEquals("Lengths differ", inList.size(), outList.size());
+        for (int i = 0; i < inList.size(); i++) {
+            assertEquals("Names of elements at index " + i + " differ", inList.get(i).getName(), outList.get(i).getName());
+            assertEquals("Values of elements at index " + i + " differ", inList.get(i).getValue(), outList.get(i).getValue());
+        }
+    }
+
+    @Path("JAXBElementArrayResource")
+    @Produces({"application/xml", "application/json"})
+    @Consumes({"application/xml", "application/json"})
+    public static class JAXBElementArrayResource extends AResource<JAXBElement<String>[]> {
+    }
+
+    private JAXBElement<String>[] getJAXBElementArray() {
+        //noinspection unchecked
+        return new JAXBElement[] {
+                new JAXBElement(QName.valueOf("element1"), String.class, "ahoj"),
+                new JAXBElement(QName.valueOf("element2"), String.class, "nazdar")
+        };
+    }
+
+    @Test
+    public void testJAXBElementArrayXMLRepresentation() {
+        _testListOrArray(false, MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Path("JAXBElementBeanResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBElementBeanResourceMediaType extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationMediaType() {
+        _test(new JaxbBean("CONTENT"), JAXBElementBeanResourceMediaType.class, MediaType.valueOf("application/foo+xml"));
+    }
+
+    @Test
+    public void testJAXBElementBeanRepresentationError() {
+        final WebTarget target = target("JAXBElementBeanResource");
+
+        final String xml = "<root><value>foo";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("JAXBElementBeanTextResource")
+    @Produces("text/xml")
+    @Consumes("text/xml")
+    public static class JAXBElementBeanTextResource extends AResource<JAXBElement<JaxbBeanType>> {
+    }
+
+    @Test
+    public void testJAXBElementBeanTextRepresentation() {
+        _test(new JaxbBean("CONTENT"), JAXBElementBeanTextResource.class, MediaType.TEXT_XML_TYPE);
+    }
+
+    @Path("JAXBTypeResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBTypeResource {
+
+        @POST
+        public JaxbBean post(final JaxbBeanType t) {
+            return new JaxbBean(t.value);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return ((ResourceConfig) super.configure()).register(new MoxyXmlFeature(SimpleBean.class));
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        super.configureClient(config);
+        config.register(new MoxyXmlFeature(SimpleBean.class));
+    }
+
+    @Test
+    public void testJAXBTypeRepresentation() {
+        final WebTarget target = target("JAXBTypeResource");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/xml"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBTypeResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBTypeResourceMediaType extends JAXBTypeResource {
+    }
+
+    @Test
+    public void testJAXBTypeRepresentationMediaType() {
+        final WebTarget target = target("JAXBTypeResourceMediaType");
+        final JaxbBean in = new JaxbBean("CONTENT");
+        final JaxbBeanType out = target.request().post(Entity.entity(in, "application/foo+xml"), JaxbBeanType.class);
+        assertEquals(in.value, out.value);
+    }
+
+    @Path("JAXBObjectResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBObjectResource {
+
+        @POST
+        public Object post(final Object o) {
+            return o;
+        }
+    }
+
+    @Provider
+    public static class JAXBObjectResolver implements ContextResolver<JAXBContext> {
+
+        public JAXBContext getContext(final Class<?> c) {
+            if (Object.class == c) {
+                try {
+                    return JAXBContext.newInstance(JaxbBean.class);
+                } catch (final JAXBException ex) {
+                    // NOOP.
+                }
+            }
+            return null;
+        }
+    }
+
+    @Test
+    public void testJAXBObjectRepresentation() {
+        final WebTarget target = target("JAXBObjectResource");
+        final Object in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/xml"), JaxbBean.class);
+        assertEquals(in, out);
+    }
+
+    @Path("JAXBObjectResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBObjectResourceMediaType extends JAXBObjectResource {
+    }
+
+    @Test
+    public void testJAXBObjectRepresentationMediaType() {
+        final WebTarget target = target("JAXBObjectResourceMediaType");
+        final Object in = new JaxbBean("CONTENT");
+        final JaxbBean out = target.request().post(Entity.entity(in, "application/foo+xml"), JaxbBean.class);
+        assertEquals(in, out);
+    }
+
+    @Test
+    public void testJAXBObjectRepresentationError() {
+        final WebTarget target = target("JAXBObjectResource");
+
+        final String xml = "<root>foo</root>";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Path("JAXBListResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBListResource {
+
+        @POST
+        public List<JaxbBean> post(final List<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("set")
+        public Set<JaxbBean> postSet(final Set<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("queue")
+        public Queue<JaxbBean> postQueue(final Queue<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("stack")
+        public Stack<JaxbBean> postStack(final Stack<JaxbBean> l) {
+            return l;
+        }
+
+        @POST
+        @Path("custom")
+        public MyArrayList<JaxbBean> postCustom(final MyArrayList<JaxbBean> l) {
+            return l;
+        }
+
+        @GET
+        public Collection<JaxbBean> get() {
+            final ArrayList<JaxbBean> l = new ArrayList<>();
+            l.add(new JaxbBean("one"));
+            l.add(new JaxbBean("two"));
+            l.add(new JaxbBean("three"));
+            return l;
+        }
+
+        @POST
+        @Path("type")
+        public List<JaxbBean> postType(final Collection<JaxbBeanType> l) {
+            final List<JaxbBean> beans = new ArrayList<>();
+            for (final JaxbBeanType t : l) {
+                beans.add(new JaxbBean(t.value));
+            }
+            return beans;
+        }
+    }
+
+    @Path("JAXBArrayResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class JAXBArrayResource {
+
+        @POST
+        public JaxbBean[] post(final JaxbBean[] l) {
+            return l;
+        }
+
+        @GET
+        public JaxbBean[] get() {
+            final ArrayList<JaxbBean> l = new ArrayList<>();
+            l.add(new JaxbBean("one"));
+            l.add(new JaxbBean("two"));
+            l.add(new JaxbBean("three"));
+            return l.toArray(new JaxbBean[l.size()]);
+        }
+
+        @POST
+        @Path("type")
+        public JaxbBean[] postType(final JaxbBeanType[] l) {
+            final List<JaxbBean> beans = new ArrayList<>();
+            for (final JaxbBeanType t : l) {
+                beans.add(new JaxbBean(t.value));
+            }
+            return beans.toArray(new JaxbBean[beans.size()]);
+        }
+    }
+
+    @Test
+    public void testJAXBArrayRepresentation() {
+        final WebTarget target = target("JAXBArrayResource");
+
+        final JaxbBean[] a = target.request().get(JaxbBean[].class);
+        JaxbBean[] b = target.request().post(Entity.entity(a, "application/xml"), JaxbBean[].class);
+        assertEquals(a.length, b.length);
+        for (int i = 0; i < a.length; i++) {
+            assertEquals(a[i], b[i]);
+        }
+
+        b = target.path("type").request().post(Entity.entity(a, "application/xml"), JaxbBean[].class);
+        assertEquals(a.length, b.length);
+        for (int i = 0; i < a.length; i++) {
+            assertEquals(a[i], b[i]);
+        }
+    }
+
+    @Path("JAXBListResourceMediaType")
+    @Produces("application/foo+xml")
+    @Consumes("application/foo+xml")
+    public static class JAXBListResourceMediaType extends JAXBListResource {
+    }
+
+    @Test
+    public void testJAXBListRepresentationMediaType() {
+        final WebTarget target = target("JAXBListResourceMediaType");
+
+        Collection<JaxbBean> a = target.request().get(
+                new GenericType<Collection<JaxbBean>>() {
+                });
+        Collection<JaxbBean> b = target.request()
+                .post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {}, "application/foo+xml"),
+                        new GenericType<Collection<JaxbBean>>() {});
+
+        assertEquals(a, b);
+
+        b = target.path("type").request().post(Entity.entity(new GenericEntity<Collection<JaxbBean>>(a) {
+        }, "application/foo+xml"), new GenericType<Collection<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new LinkedList<>(a);
+        b = target.path("queue").request().post(Entity.entity(new GenericEntity<Queue<JaxbBean>>((Queue<JaxbBean>) a) {
+        }, "application/foo+xml"), new GenericType<Queue<JaxbBean>>() {
+        });
+        assertEquals(a, b);
+
+        a = new HashSet<>(a);
+        b = target.path("set").request().post(Entity.entity(new GenericEntity<Set<JaxbBean>>((Set<JaxbBean>) a) {
+        }, "application/foo+xml"), new GenericType<Set<JaxbBean>>() {
+        });
+        final Comparator<JaxbBean> c = new Comparator<JaxbBean>() {
+            @Override
+            public int compare(final JaxbBean t, final JaxbBean t1) {
+                return t.value.compareTo(t1.value);
+            }
+        };
+        final TreeSet<JaxbBean> t1 = new TreeSet<>(c);
+        final TreeSet<JaxbBean> t2 = new TreeSet<>(c);
+        t1.addAll(a);
+        t2.addAll(b);
+        assertEquals(t1, t2);
+
+        final Stack<JaxbBean> s = new Stack<>();
+        s.addAll(a);
+        b = target.path("stack").request().post(Entity.entity(new GenericEntity<Stack<JaxbBean>>(s) {
+        }, "application/foo+xml"), new GenericType<Stack<JaxbBean>>() {
+        });
+        assertEquals(s, b);
+
+        a = new MyArrayList<>(a);
+        b = target.path("custom").request()
+                .post(Entity.entity(new GenericEntity<MyArrayList<JaxbBean>>((MyArrayList<JaxbBean>) a) {
+                }, "application/foo+xml"), new GenericType<MyArrayList<JaxbBean>>() {
+                });
+        assertEquals(a, b);
+    }
+
+    @Test
+    public void testJAXBListRepresentationError() {
+        final WebTarget target = target("JAXBListResource");
+
+        final String xml = "<root><value>foo";
+        final Response cr = target.request().post(Entity.entity(xml, "application/xml"));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    public static class SimpleBean {
+
+        private String value;
+
+        public SimpleBean() {
+        }
+
+        public SimpleBean(final String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(final String value) {
+            this.value = value;
+        }
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    @XmlRootElement
+    public static class ComplexJaxbBean {
+
+        private Object simpleBean;
+
+        public ComplexJaxbBean() {
+        }
+
+        public ComplexJaxbBean(final Object simpleBean) {
+            this.simpleBean = simpleBean;
+        }
+
+        public Object getSimpleBean() {
+            return simpleBean;
+        }
+
+        public void setSimpleBean(final Object simpleBean) {
+            this.simpleBean = simpleBean;
+        }
+    }
+
+    @Path("AdditionalClassesResource")
+    @Produces("application/xml")
+    @Consumes("application/xml")
+    public static class AdditionalClassesResource {
+
+        @GET
+        public ComplexJaxbBean get() {
+            return new ComplexJaxbBean(new SimpleBean("foo"));
+        }
+    }
+
+    @Test
+    public void testAdditionalClasses() throws Exception {
+        final ComplexJaxbBean nonJaxbBean = target("AdditionalClassesResource").request().get(ComplexJaxbBean.class);
+        final Object simpleBean = nonJaxbBean.getSimpleBean();
+
+        assertThat(simpleBean, notNullValue());
+        assertThat(simpleBean, instanceOf(SimpleBean.class));
+        assertThat("foo", equalTo(((SimpleBean) simpleBean).getValue()));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/DefaultFilteringScope.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/DefaultFilteringScope.java
new file mode 100644
index 0000000..fd630b5
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/DefaultFilteringScope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * @author Michal Gajdos
+ */
+public class DefaultFilteringScope extends AnnotationLiteral<EntityFiltering> implements EntityFiltering {
+
+    /**
+     * Get an instance of {@link EntityFiltering} annotation.
+     */
+    public DefaultFilteringScope() {
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EmptyEntityTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EmptyEntityTest.java
new file mode 100644
index 0000000..43c3562
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EmptyEntityTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.EmptyEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.NonEmptyEntity;
+
+import org.junit.Test;
+
+/**
+ * Use-cases to check whether empty class causes problems (JERSEY-2824 reproducer).
+ *
+ * @author Michal Gajdos
+ */
+public class EmptyEntityTest extends EntityFilteringTest {
+
+    @Path("/")
+    @Consumes("entity/filtering")
+    @Produces("entity/filtering")
+    public static class Resource {
+
+        @Path("nonEmptyEntity")
+        @GET
+        public NonEmptyEntity nonEmptyEntity() {
+            return NonEmptyEntity.INSTANCE;
+        }
+
+        @Path("emptyEntity")
+        @GET
+        public EmptyEntity emptyEntity() {
+            return new EmptyEntity();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                // Resources.
+                .register(Resource.class)
+                // Providers.
+                .register(EntityFilteringFeature.class)
+                .register(FilteringMessageBodyProvider.class);
+    }
+
+    @Test
+    public void testNonEmptyEntity() throws Exception {
+        final String fields = target("nonEmptyEntity").request().get(String.class);
+
+        assertSameFields(fields, "value");
+    }
+
+    @Test
+    public void testEmptyEntity() throws Exception {
+        final String fields = target("emptyEntity").request().get(String.class);
+
+        assertSameFields(fields, "");
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringClientTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringClientTest.java
new file mode 100644
index 0000000..1605035
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringClientTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringOnClassEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(ConcurrentRunner.class)
+public class EntityFilteringClientTest extends EntityFilteringTest {
+
+    public static final MediaType ENTITY_FILTERING = new MediaType("entity", "filtering");
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                // Resources.
+                .register(Resource.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(EntityFilteringFeature.class).register(FilteringMessageBodyProvider.class);
+    }
+
+    @Path("/")
+    @Consumes("entity/filtering")
+    @Produces("entity/filtering")
+    public static class Resource {
+
+        @POST
+        public String post(final String value) {
+            return value;
+        }
+    }
+
+    @Test
+    public void testEntityAnnotationsPrimaryView() throws Exception {
+        final String fields = target()
+                .request()
+                .post(Entity.entity(
+                                new OneFilteringOnClassEntity(),
+                                ENTITY_FILTERING,
+                                new Annotation[] {PrimaryDetailedView.Factory.get()}),
+                        String.class);
+
+        assertSameFields(fields, "field,accessor,property,subEntities.field2,subEntities.property2,subEntities.property1,"
+                + "subEntities.field1,defaultEntities.field,defaultEntities.property");
+    }
+
+    @Test
+    public void testEntityAnnotationsDefaultView() throws Exception {
+        final String fields = target()
+                .request()
+                .post(Entity.entity(new OneFilteringOnClassEntity(),
+                                ENTITY_FILTERING,
+                                new Annotation[] {new DefaultFilteringScope()}),
+                        String.class);
+
+        assertThat(fields, equalTo(""));
+    }
+
+    @Test
+    public void testEntityAnnotationsInvalidView() throws Exception {
+        final String fields = target()
+                .request()
+                .post(Entity.entity(
+                                new OneFilteringOnClassEntity(),
+                                ENTITY_FILTERING,
+                                new Annotation[] {CustomAnnotationLiteral.INSTANCE}),
+                        String.class);
+
+        assertThat(fields, equalTo(""));
+    }
+
+    @Test
+    public void testConfigurationPrimaryView() throws Exception {
+        testConfiguration("field,accessor,property,subEntities.field2,subEntities.property2,subEntities.property1,"
+                + "subEntities.field1,defaultEntities.field,defaultEntities.property", PrimaryDetailedView.Factory.get());
+    }
+
+    @Test
+    public void testConfigurationDefaultView() throws Exception {
+        testConfiguration("", new DefaultFilteringScope());
+    }
+
+    @Test
+    public void testConfigurationMultipleViews() throws Exception {
+        testConfiguration("field,accessor,property,subEntities.field2,subEntities.property2,subEntities.property1,"
+                        + "subEntities.field1,defaultEntities.field,defaultEntities.property", PrimaryDetailedView.Factory.get(),
+                CustomAnnotationLiteral.INSTANCE);
+    }
+
+    private void testConfiguration(final String expected, final Annotation... annotations) {
+        final ClientConfig config = new ClientConfig()
+                .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, annotations.length == 1 ? annotations[0] : annotations);
+        configureClient(config);
+
+        final String fields = ClientBuilder.newClient(config)
+                .target(getBaseUri())
+                .request()
+                .post(Entity.entity(new OneFilteringOnClassEntity(), ENTITY_FILTERING), String.class);
+
+        assertSameFields(fields, expected);
+    }
+
+    @Test
+    public void testInvalidConfiguration() throws Exception {
+        final ClientConfig config = new ClientConfig()
+                .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, "invalid_value");
+        configureClient(config);
+
+        final String fields =
+                ClientBuilder.newClient(config)
+                        .target(getBaseUri())
+                        .request()
+                        .post(Entity.entity(new OneFilteringOnClassEntity(), ENTITY_FILTERING), String.class);
+
+        assertThat(fields, equalTo(""));
+    }
+
+    @Test
+    public void testEntityAnnotationsOverConfiguration() throws Exception {
+        final ClientConfig config = new ClientConfig()
+                .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, SecondaryDetailedView.Factory.get());
+        configureClient(config);
+
+        final String fields = ClientBuilder.newClient(config)
+                .target(getBaseUri())
+                .request()
+                .post(Entity.entity(
+                                new ManyFilteringsOnClassEntity(),
+                                ENTITY_FILTERING,
+                                new Annotation[] {PrimaryDetailedView.Factory.get()}),
+                        String.class);
+
+        assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                + ".property");
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnClassTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnClassTest.java
new file mode 100644
index 0000000..912c24d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnClassTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringOnClassEntity;
+
+import org.junit.Test;
+
+/**
+ * Use-cases with entity-filtering annotations on class.
+ *
+ * @author Michal Gajdos
+ */
+public class EntityFilteringOnClassTest extends EntityFilteringTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                // Resources.
+                .register(Resource.class)
+                // Providers.
+                .register(EntityFilteringFeature.class)
+                .register(FilteringMessageBodyProvider.class);
+    }
+
+    @Path("/")
+    @Consumes("entity/filtering")
+    @Produces("entity/filtering")
+    public static class Resource {
+
+        @GET
+        @Path("OneFilteringEntity")
+        @PrimaryDetailedView
+        public OneFilteringOnClassEntity getOneFilteringEntity() {
+            return new OneFilteringOnClassEntity();
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultView")
+        public OneFilteringOnClassEntity getOneFilteringEntityDefaultView() {
+            return new OneFilteringOnClassEntity();
+        }
+
+        @POST
+        @Path("OneFilteringEntity")
+        public String postOneFilteringEntity(final String value) {
+            return value;
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultViewResponse")
+        public Response getOneFilteringEntityDefaultViewResponse() {
+            return Response.ok().entity(new OneFilteringOnClassEntity(), new Annotation[] {new DefaultFilteringScope()}).build();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityPrimaryView")
+        @PrimaryDetailedView
+        public ManyFilteringsOnClassEntity getManyFilteringsEntityPrimaryView() {
+            return new ManyFilteringsOnClassEntity();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntitySecondaryView")
+        @SecondaryDetailedView
+        public ManyFilteringsOnClassEntity getManyFilteringsEntitySecondaryView() {
+            return new ManyFilteringsOnClassEntity();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityDefaultView")
+        public ManyFilteringsOnClassEntity getManyFilteringsEntityDefaultView() {
+            return new ManyFilteringsOnClassEntity();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityManyViews")
+        @PrimaryDetailedView
+        @SecondaryDetailedView
+        public ManyFilteringsOnClassEntity getManyFilteringsEntityManyViews() {
+            return new ManyFilteringsOnClassEntity();
+        }
+    }
+
+    @Test
+    public void testOneEntityFilteringOnClass() throws Exception {
+        final String fields = target("OneFilteringEntity").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,subEntities.field2,subEntities.property2,subEntities.property1,"
+                + "subEntities.field1,defaultEntities.field,defaultEntities.property");
+    }
+
+    @Test
+    public void testOneEntityFilteringOnClassDefaultViewResponse() throws Exception {
+        final String fields = target("OneFilteringEntityDefaultViewResponse").request().get(String.class);
+
+        assertSameFields(fields, "");
+    }
+
+    @Test
+    public void testOneEntityFilteringOnClassDefaultView() throws Exception {
+        final String fields = target("OneFilteringEntityDefaultView").request().get(String.class);
+
+        assertSameFields(fields, "");
+    }
+
+    @Test
+    public void testMultipleViewsOnClass() throws Exception {
+        testOneEntityFilteringOnClass();
+        testOneEntityFilteringOnClassDefaultView();
+    }
+
+    @Test
+    public void testManyFilteringsEntityPrimaryView() throws Exception {
+        final String fields = target("ManyFilteringsEntityPrimaryView").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                + ".property");
+    }
+
+    @Test
+    public void testManyFilteringsEntitySecondaryView() throws Exception {
+        final String fields = target("ManyFilteringsEntitySecondaryView").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,manyEntities.field2,manyEntities.property2,manyEntities.field1,"
+                + "oneEntities.property2,oneEntities.field1,defaultEntities.field,defaultEntities.property");
+    }
+
+    @Test
+    public void testManyFilteringsEntityDefaultView() throws Exception {
+        final String fields = target("ManyFilteringsEntityDefaultView").request().get(String.class);
+
+        assertSameFields(fields, "");
+    }
+
+    @Test
+    public void testManyFilteringsEntityManyViews() throws Exception {
+        final String fields = target("ManyFilteringsEntityManyViews").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,manyEntities.field2,manyEntities.property2,manyEntities.property1,"
+                + "manyEntities.field1,oneEntities.field2,oneEntities.property2,oneEntities.property1,oneEntities.field1,"
+                + "defaultEntities.field,defaultEntities.property");
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnPropertiesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnPropertiesTest.java
new file mode 100644
index 0000000..22603ab
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnPropertiesTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnPropertiesEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringOnPropertiesEntity;
+
+import org.junit.Test;
+
+/**
+ * Use-cases with entity-filtering annotations on properties.
+ *
+ * @author Michal Gajdos
+ */
+public class EntityFilteringOnPropertiesTest extends EntityFilteringTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                // Resources.
+                .register(Resource.class)
+                // Providers.
+                .register(EntityFilteringFeature.class)
+                .register(FilteringMessageBodyProvider.class);
+    }
+
+    @Path("/")
+    @Consumes("entity/filtering")
+    @Produces("entity/filtering")
+    public static class Resource {
+
+        @GET
+        @Path("OneFilteringEntity")
+        @PrimaryDetailedView
+        public OneFilteringOnPropertiesEntity getOneFilteringEntity() {
+            return new OneFilteringOnPropertiesEntity();
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultView")
+        public OneFilteringOnPropertiesEntity getOneFilteringEntityDefaultView() {
+            return new OneFilteringOnPropertiesEntity();
+        }
+
+        @POST
+        @Path("OneFilteringEntity")
+        public String postOneFilteringEntity(final String value) {
+            return value;
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultViewResponse")
+        public Response getOneFilteringEntityDefaultViewResponse() {
+            return Response.ok().entity(new OneFilteringOnPropertiesEntity(), new Annotation[] {new DefaultFilteringScope()})
+                    .build();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityPrimaryView")
+        @PrimaryDetailedView
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntityPrimaryView() {
+            return new ManyFilteringsOnPropertiesEntity();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntitySecondaryView")
+        @SecondaryDetailedView
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntitySecondaryView() {
+            return new ManyFilteringsOnPropertiesEntity();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityDefaultView")
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntityDefaultView() {
+            return new ManyFilteringsOnPropertiesEntity();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityManyViews")
+        @PrimaryDetailedView
+        @SecondaryDetailedView
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntityManyViews() {
+            return new ManyFilteringsOnPropertiesEntity();
+        }
+    }
+
+    @Test
+    public void testOneEntityFilteringOnProperties() throws Exception {
+        final String fields = target("OneFilteringEntity").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,subEntities.field2,subEntities.property2,subEntities.property1,"
+                + "subEntities.field1,defaultEntities.field,defaultEntities.property");
+    }
+
+    @Test
+    public void testOneEntityFilteringOnPropertiesDefaultViewResponse() throws Exception {
+        final String fields = target("OneFilteringEntityDefaultViewResponse").request().get(String.class);
+
+        assertSameFields(fields, "field");
+    }
+
+    @Test
+    public void testOneEntityFilteringOnPropertiesDefaultView() throws Exception {
+        final String fields = target("OneFilteringEntityDefaultView").request().get(String.class);
+
+        assertSameFields(fields, "field");
+    }
+
+    @Test
+    public void testManyFilteringsEntityPrimaryView() throws Exception {
+        final String fields = target("ManyFilteringsEntityPrimaryView").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,oneEntities.field2,oneEntities.property2,oneEntities.property1,"
+                + "oneEntities.field1,defaultEntities.field,defaultEntities.property");
+    }
+
+    @Test
+    public void testManyFilteringsEntitySecondaryView() throws Exception {
+        final String fields = target("ManyFilteringsEntitySecondaryView").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,manyEntities.field2,manyEntities.property2,manyEntities.field1,"
+                + "oneEntities.property2,oneEntities.field1");
+    }
+
+    @Test
+    public void testManyFilteringsEntityDefaultView() throws Exception {
+        final String fields = target("ManyFilteringsEntityDefaultView").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor");
+    }
+
+    @Test
+    public void testManyFilteringsEntityManyViews() throws Exception {
+        final String fields = target("ManyFilteringsEntityManyViews").request().get(String.class);
+
+        assertSameFields(fields, "field,accessor,property,manyEntities.field2,manyEntities.property2,manyEntities.property1,"
+                + "manyEntities.field1,oneEntities.field2,oneEntities.property2,oneEntities.property1,oneEntities.field1,"
+                + "defaultEntities.field,defaultEntities.property");
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringScopesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringScopesTest.java
new file mode 100644
index 0000000..5f910fb
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringScopesTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ComplexEntity;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EntityFilteringScopesTest extends EntityFilteringTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                // Resources.
+                .register(Resource.class)
+                // Providers.
+                .register(EntityFilteringFeature.class)
+                .register(FilteringMessageBodyProvider.class);
+    }
+
+    @Path("/")
+    @Consumes("entity/filtering")
+    @Produces("entity/filtering")
+    public static class Resource {
+
+        @GET
+        @PrimaryDetailedView
+        public ComplexEntity get() {
+            return new ComplexEntity();
+        }
+    }
+
+    /**
+     * Primary -> Default -> Primary.
+     */
+    @Test
+    public void testEntityFilteringScopes() throws Exception {
+        final String fields = target().request().get(String.class);
+
+        assertSameFields(fields, "accessor,property,field.field,field.accessor,field.property.accessor,field.property.property");
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringServerTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringServerTest.java
new file mode 100644
index 0000000..4d8303b
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringServerTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnClassEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        EntityFilteringServerTest.ConfigurationServerTest.class,
+        EntityFilteringServerTest.ConfigurationDefaultViewServerTest.class,
+        EntityFilteringServerTest.AnnotationsServerTest.class,
+        EntityFilteringServerTest.AnnotationsOverConfigurationServerTest.class
+})
+public class EntityFilteringServerTest {
+
+    @Path("/")
+    @Produces("entity/filtering")
+    public static class Resource {
+
+        @GET
+        @Path("configuration")
+        public ManyFilteringsOnClassEntity getConfiguration() {
+            return new ManyFilteringsOnClassEntity();
+        }
+
+        @GET
+        @Path("configurationOverResource")
+        @SecondaryDetailedView
+        public ManyFilteringsOnClassEntity getConfigurationOverResource() {
+            return new ManyFilteringsOnClassEntity();
+        }
+
+        @GET
+        @Path("annotations")
+        public Response getAnnotations() {
+            return Response
+                    .ok()
+                    .entity(new ManyFilteringsOnClassEntity(), new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+
+        @GET
+        @Path("annotationsOverConfiguration")
+        public Response getAnnotationsOverConfiguration() {
+            return Response
+                    .ok()
+                    .entity(new ManyFilteringsOnClassEntity(), new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+
+        @GET
+        @Path("annotationsOverResource")
+        @SecondaryDetailedView
+        public Response getAnnotationsOverResource() {
+            return Response
+                    .ok()
+                    .entity(new ManyFilteringsOnClassEntity(), new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+
+        @GET
+        @Path("annotationsOverConfigurationOverResource")
+        @SecondaryDetailedView
+        public Response getAnnotationsOverConfigurationOverResource() {
+            return Response
+                    .ok()
+                    .entity(new ManyFilteringsOnClassEntity(), new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+    }
+
+    private static class FilteringResourceConfig extends ResourceConfig {
+
+        private FilteringResourceConfig() {
+            // Resources.
+            register(Resource.class);
+
+            // Providers.
+            register(EntityFilteringFeature.class);
+            register(FilteringMessageBodyProvider.class);
+        }
+    }
+
+    public static class ConfigurationServerTest extends EntityFilteringTest {
+
+        @Override
+        protected Application configure() {
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+
+            return new FilteringResourceConfig()
+                    // Properties
+                    .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, PrimaryDetailedView.Factory.get());
+        }
+
+        @Test
+        public void testConfiguration() throws Exception {
+            final String fields = target("configuration").request().get(String.class);
+
+            assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                    + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                    + ".property");
+        }
+
+        @Test
+        public void testConfigurationOverResource() throws Exception {
+            final String fields = target("configurationOverResource").request().get(String.class);
+
+            assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                    + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                    + ".property");
+        }
+    }
+
+    public static class ConfigurationDefaultViewServerTest extends EntityFilteringTest {
+
+        @Override
+        protected Application configure() {
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+
+            return new FilteringResourceConfig();
+        }
+
+        @Test
+        public void testConfiguration() throws Exception {
+            final String fields = target("configuration").request().get(String.class);
+
+            assertSameFields(fields, "");
+        }
+    }
+
+    public static class AnnotationsServerTest extends EntityFilteringTest {
+
+        @Override
+        protected Application configure() {
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+
+            return new FilteringResourceConfig();
+        }
+
+        @Test
+        public void testAnnotations() throws Exception {
+            final String fields = target("annotations").request().get(String.class);
+
+            assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                    + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                    + ".property");
+        }
+
+        @Test
+        public void testAnnotationsOverResource() throws Exception {
+            final String fields = target("annotationsOverResource").request().get(String.class);
+
+            assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                    + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                    + ".property");
+        }
+    }
+
+    public static class AnnotationsOverConfigurationServerTest extends EntityFilteringTest {
+
+        @Override
+        protected Application configure() {
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+
+            return new FilteringResourceConfig()
+                    // Properties
+                    .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new DefaultFilteringScope());
+        }
+
+        @Test
+        public void testAnnotationsOverConfiguration() throws Exception {
+            final String fields = target("annotationsOverConfiguration").request().get(String.class);
+
+            assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                    + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                    + ".property");
+        }
+
+        @Test
+        public void testAnnotationsOverConfigurationOverResource() throws Exception {
+            final String fields = target("annotationsOverConfigurationOverResource").request().get(String.class);
+
+            assertSameFields(fields, "field,accessor,property,manyEntities.property1,manyEntities.field1,oneEntities.field2,"
+                    + "oneEntities.property2,oneEntities.property1,oneEntities.field1,defaultEntities.field,defaultEntities"
+                    + ".property");
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringTest.java
new file mode 100644
index 0000000..b450e0c
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2014, 2018 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.entity.filtering;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.glassfish.jersey.test.JerseyTest;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Common parent class for Entity Filtering tests.
+ *
+ * @author Michal Gajdos
+ */
+abstract class EntityFilteringTest extends JerseyTest {
+
+    /**
+     * Test that (filtered) fields of given strings are equal (fields are separated by commas).
+     *
+     * @param actual actual fields from response received from server.
+     * @param expected expected fields.
+     */
+    static void assertSameFields(final String actual, final String expected) {
+        final Set<String> actualSet = Arrays.stream(actual.split(",")).collect(Collectors.toSet());
+        final Set<String> expectedSet = Arrays.stream(expected.split(",")).collect(Collectors.toSet());
+
+        assertThat(actualSet, equalTo(expectedSet));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/FilteringMessageBodyProvider.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/FilteringMessageBodyProvider.java
new file mode 100644
index 0000000..dcb6ba5
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/FilteringMessageBodyProvider.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+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 javax.inject.Inject;
+
+import org.glassfish.jersey.message.filtering.spi.FilteringHelper;
+import org.glassfish.jersey.message.filtering.spi.ObjectGraph;
+import org.glassfish.jersey.message.filtering.spi.ObjectProvider;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+@Consumes("entity/filtering")
+@Produces("entity/filtering")
+public class FilteringMessageBodyProvider implements MessageBodyReader<Object>, MessageBodyWriter<Object> {
+
+    private static final Logger LOGGER = Logger.getLogger(FilteringMessageBodyProvider.class.getName());
+
+    @Inject
+    private javax.inject.Provider<ObjectProvider<ObjectGraph>> provider;
+
+    @Override
+    public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                              final MediaType mediaType) {
+        return String.class != type;
+    }
+
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return String.class != type;
+    }
+
+    @Override
+    public long getSize(final Object o, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public Object readFrom(final Class<Object> type, final Type genericType, final Annotation[] annotations,
+                           final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders,
+                           final InputStream entityStream) throws IOException, WebApplicationException {
+        try {
+            final ObjectGraph objectGraph = provider.get()
+                    .getFilteringObject(FilteringHelper.getEntityClass(genericType), false, annotations);
+
+            return objectGraphToString(objectGraph);
+        } catch (final Throwable t) {
+            LOGGER.log(Level.WARNING, "Error during reading an object graph.", t);
+            return "ERROR: " + t.getMessage();
+        }
+    }
+
+    @Override
+    public void writeTo(final Object o, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        final ObjectGraph objectGraph = provider.get()
+                .getFilteringObject(FilteringHelper.getEntityClass(genericType), true, annotations);
+
+        try {
+            entityStream.write(objectGraphToString(objectGraph).getBytes());
+        } catch (final Throwable t) {
+            LOGGER.log(Level.WARNING, "Error during writing an object graph.", t);
+        }
+    }
+
+    private static String objectGraphToString(final ObjectGraph objectGraph) {
+        final StringBuilder sb = new StringBuilder();
+        for (final String field : objectGraphToFields("", objectGraph)) {
+            if (!field.contains("Transient")) {
+                sb.append(field).append(',');
+            }
+        }
+        if (sb.length() > 0) {
+            sb.delete(sb.length() - 1, sb.length());
+        }
+        return sb.toString();
+    }
+
+    private static List<String> objectGraphToFields(final String prefix, final ObjectGraph objectGraph) {
+        final List<String> fields = new ArrayList<>();
+
+        // Fields.
+        for (final String field : objectGraph.getFields()) {
+            fields.add(prefix + field);
+        }
+
+        for (final Map.Entry<String, ObjectGraph> entry : objectGraph.getSubgraphs().entrySet()) {
+            fields.addAll(objectGraphToFields(prefix + entry.getKey() + ".", entry.getValue()));
+        }
+
+        return fields;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/PrimaryDetailedView.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/PrimaryDetailedView.java
new file mode 100644
index 0000000..340a343
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/PrimaryDetailedView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface PrimaryDetailedView {
+
+    public static class Factory extends AnnotationLiteral<PrimaryDetailedView> implements PrimaryDetailedView {
+
+        private Factory() {
+        }
+
+        public static PrimaryDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/SecondaryDetailedView.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/SecondaryDetailedView.java
new file mode 100644
index 0000000..8bd206f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/SecondaryDetailedView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface SecondaryDetailedView {
+
+    public static class Factory extends AnnotationLiteral<SecondaryDetailedView> implements SecondaryDetailedView {
+
+        private Factory() {
+        }
+
+        public static SecondaryDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/TertiaryDetailedView.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/TertiaryDetailedView.java
new file mode 100644
index 0000000..3b9a512
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/TertiaryDetailedView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface TertiaryDetailedView {
+
+    public static class Factory extends AnnotationLiteral<TertiaryDetailedView> implements TertiaryDetailedView {
+
+        private Factory() {
+        }
+
+        public static TertiaryDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexEntity.java
new file mode 100644
index 0000000..a6a36a2
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexEntity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * Primary detailed view ONLY.
+ *
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+// Entity Filtering.
+@PrimaryDetailedView
+public class ComplexEntity {
+
+    public static final ComplexEntity INSTANCE;
+
+    static {
+        INSTANCE = new ComplexEntity();
+        INSTANCE.field = ComplexSubEntity.INSTANCE;
+        INSTANCE.property = "property";
+    }
+
+    @XmlElement
+    public ComplexSubEntity field;
+
+    private String property;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    public String getAccessor() {
+        return accessorTransient == null ? property + property : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubEntity.java
new file mode 100644
index 0000000..7676e3c
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubEntity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * Default view ONLY.
+ *
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class ComplexSubEntity {
+
+    public static final ComplexSubEntity INSTANCE;
+
+    static {
+        INSTANCE = new ComplexSubEntity();
+        INSTANCE.field = "field";
+        INSTANCE.property = ComplexSubSubEntity.INSTANCE;
+    }
+
+    @XmlElement
+    public String field;
+
+    private ComplexSubSubEntity property;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    public ComplexSubSubEntity getProperty() {
+        return property;
+    }
+
+    public void setProperty(final ComplexSubSubEntity property) {
+        this.property = property;
+    }
+
+    public String getAccessor() {
+        return accessorTransient == null ? field + field : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubSubEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubSubEntity.java
new file mode 100644
index 0000000..be709e0
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubSubEntity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * Mixed views.
+ *
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class ComplexSubSubEntity {
+
+    public static final ComplexSubSubEntity INSTANCE;
+
+    static {
+        INSTANCE = new ComplexSubSubEntity();
+        INSTANCE.field = "field";
+        INSTANCE.property = "property";
+    }
+
+    @XmlElement
+    @SecondaryDetailedView
+    public String field;
+
+    private String property;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    @PrimaryDetailedView
+    public String getAccessor() {
+        return accessorTransient == null ? field + field : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/DefaultFilteringSubEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/DefaultFilteringSubEntity.java
new file mode 100644
index 0000000..fcd3888
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/DefaultFilteringSubEntity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+
+/**
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class DefaultFilteringSubEntity {
+
+    public static final DefaultFilteringSubEntity INSTANCE;
+
+    static {
+        INSTANCE = new DefaultFilteringSubEntity();
+        INSTANCE.field = true;
+        INSTANCE.property = 20L;
+    }
+
+    @XmlElement
+    public boolean field;
+    private Long property;
+
+    public Long getProperty() {
+        return property;
+    }
+
+    public void setProperty(final Long property) {
+        this.property = property;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/EmptyEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/EmptyEntity.java
new file mode 100644
index 0000000..7e8389c
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/EmptyEntity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.domain;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EmptyEntity {
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/FilteredClassEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/FilteredClassEntity.java
new file mode 100644
index 0000000..b1ec5f8
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/FilteredClassEntity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.TertiaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+
+/**
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+// Entity Filtering.
+@TertiaryDetailedView
+public class FilteredClassEntity {
+
+    public static final FilteredClassEntity INSTANCE;
+
+    static {
+        INSTANCE = new FilteredClassEntity();
+        INSTANCE.field = 40;
+        INSTANCE.property = "property";
+    }
+
+    @XmlElement
+    public int field;
+    private String property;
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnClassEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnClassEntity.java
new file mode 100644
index 0000000..8221fb9
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnClassEntity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+// Entity Filtering.
+@PrimaryDetailedView
+@SecondaryDetailedView
+public class ManyFilteringsOnClassEntity {
+
+    public static final ManyFilteringsOnClassEntity INSTANCE;
+
+    static {
+        INSTANCE = new ManyFilteringsOnClassEntity();
+        INSTANCE.field = 50;
+        INSTANCE.property = "property";
+        INSTANCE.defaultEntities = Collections.singletonList(DefaultFilteringSubEntity.INSTANCE);
+        INSTANCE.oneEntities = Collections.singletonList(OneFilteringSubEntity.INSTANCE);
+        INSTANCE.manyEntities = Collections.singletonList(ManyFilteringsSubEntity.INSTANCE);
+        INSTANCE.filtered = FilteredClassEntity.INSTANCE;
+    }
+
+    @XmlElement
+    public int field;
+    private String property;
+
+    @XmlElement
+    public List<DefaultFilteringSubEntity> defaultEntities;
+
+    @XmlElement
+    public List<OneFilteringSubEntity> oneEntities;
+    @XmlElement
+    public List<ManyFilteringsSubEntity> manyEntities;
+
+    @XmlElement
+    public FilteredClassEntity filtered;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    public String getAccessor() {
+        return accessorTransient == null ? property + property : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnPropertiesEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnPropertiesEntity.java
new file mode 100644
index 0000000..f59b87d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnPropertiesEntity.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * @author Michal Gajdos
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class ManyFilteringsOnPropertiesEntity {
+
+    public static final ManyFilteringsOnPropertiesEntity INSTANCE;
+
+    static {
+        INSTANCE = new ManyFilteringsOnPropertiesEntity();
+        INSTANCE.field = 90;
+        INSTANCE.property = "property";
+        INSTANCE.defaultEntities = Collections.singletonList(DefaultFilteringSubEntity.INSTANCE);
+        INSTANCE.oneEntities = Collections.singletonList(OneFilteringSubEntity.INSTANCE);
+        INSTANCE.manyEntities = Collections.singletonList(ManyFilteringsSubEntity.INSTANCE);
+        INSTANCE.filtered = FilteredClassEntity.INSTANCE;
+    }
+
+    @XmlElement
+    public int field;
+    private String property;
+
+    @XmlElement
+    @PrimaryDetailedView
+    public List<DefaultFilteringSubEntity> defaultEntities;
+
+    @XmlElement
+    @PrimaryDetailedView
+    @SecondaryDetailedView
+    public List<OneFilteringSubEntity> oneEntities;
+
+    @XmlElement
+    @SecondaryDetailedView
+    public List<ManyFilteringsSubEntity> manyEntities;
+
+    @XmlElement
+    @PrimaryDetailedView
+    @SecondaryDetailedView
+    public FilteredClassEntity filtered;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    @PrimaryDetailedView
+    @SecondaryDetailedView
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    public String getAccessor() {
+        return accessorTransient == null ? property + property : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsSubEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsSubEntity.java
new file mode 100644
index 0000000..b326abc
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsSubEntity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+
+/**
+* @author Michal Gajdos
+*/
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class ManyFilteringsSubEntity {
+
+    public static final ManyFilteringsSubEntity INSTANCE;
+
+    static {
+        INSTANCE = new ManyFilteringsSubEntity();
+        INSTANCE.field1 = 60;
+        INSTANCE.field2 = 70;
+        INSTANCE.property1 = "property1";
+        INSTANCE.property2 = "property2";
+    }
+
+    @XmlElement
+    public int field1;
+
+    @XmlElement
+    @SecondaryDetailedView
+    public int field2;
+
+    private String property1;
+    private String property2;
+
+    @PrimaryDetailedView
+    public String getProperty1() {
+        return property1;
+    }
+
+    public void setProperty1(final String property1) {
+        this.property1 = property1;
+    }
+
+    @SecondaryDetailedView
+    public String getProperty2() {
+        return property2;
+    }
+
+    @PrimaryDetailedView
+    public void setProperty2(final String property2) {
+        this.property2 = property2;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/NonEmptyEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/NonEmptyEntity.java
new file mode 100644
index 0000000..78f0098
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/NonEmptyEntity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.domain;
+
+/**
+ * @author Michal Gajdos
+ */
+public class NonEmptyEntity {
+
+    public static final NonEmptyEntity INSTANCE;
+
+    static {
+        INSTANCE = new NonEmptyEntity();
+        INSTANCE.setValue("foo");
+    }
+
+    private String value;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(final String value) {
+        this.value = value;
+    }
+
+    public EmptyEntity getEmptyEntity() {
+        return null;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnClassEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnClassEntity.java
new file mode 100644
index 0000000..55be9fc
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnClassEntity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+* @author Michal Gajdos
+*/
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+// Entity Filtering.
+@PrimaryDetailedView
+public class OneFilteringOnClassEntity {
+
+    public static final OneFilteringOnClassEntity INSTANCE;
+
+    static {
+        INSTANCE = new OneFilteringOnClassEntity();
+        INSTANCE.field = 10;
+        INSTANCE.property = "property";
+        INSTANCE.defaultEntities = Collections.singletonList(DefaultFilteringSubEntity.INSTANCE);
+        INSTANCE.subEntities = Collections.singletonList(OneFilteringSubEntity.INSTANCE);
+        INSTANCE.filtered = FilteredClassEntity.INSTANCE;
+    }
+
+    @XmlElement
+    public int field;
+    private String property;
+
+    private List<DefaultFilteringSubEntity> defaultEntities;
+    private List<OneFilteringSubEntity> subEntities;
+
+    private FilteredClassEntity filtered;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    public List<DefaultFilteringSubEntity> getDefaultEntities() {
+        return defaultEntities;
+    }
+
+    public void setDefaultEntities(final List<DefaultFilteringSubEntity> defaultEntities) {
+        this.defaultEntities = defaultEntities;
+    }
+
+    public List<OneFilteringSubEntity> getSubEntities() {
+        return subEntities;
+    }
+
+    public void setSubEntities(final List<OneFilteringSubEntity> subEntities) {
+        this.subEntities = subEntities;
+    }
+
+    public String getAccessor() {
+        return accessorTransient == null ? property + property : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+
+    public FilteredClassEntity getFiltered() {
+        return filtered;
+    }
+
+    public void setFiltered(final FilteredClassEntity filtered) {
+        this.filtered = filtered;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnPropertiesEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnPropertiesEntity.java
new file mode 100644
index 0000000..43809e4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnPropertiesEntity.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+* @author Michal Gajdos
+*/
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class OneFilteringOnPropertiesEntity {
+
+    public static final OneFilteringOnPropertiesEntity INSTANCE;
+
+    static {
+        INSTANCE = new OneFilteringOnPropertiesEntity();
+        INSTANCE.field = 80;
+        INSTANCE.property = "property";
+        INSTANCE.defaultEntities = Collections.singletonList(DefaultFilteringSubEntity.INSTANCE);
+        INSTANCE.subEntities = Collections.singletonList(OneFilteringSubEntity.INSTANCE);
+        INSTANCE.filtered = FilteredClassEntity.INSTANCE;
+    }
+
+    @XmlElement
+    public int field;
+    private String property;
+
+    private List<DefaultFilteringSubEntity> defaultEntities;
+
+    @PrimaryDetailedView
+    private List<OneFilteringSubEntity> subEntities;
+
+    @PrimaryDetailedView
+    private FilteredClassEntity filtered;
+
+    @XmlTransient
+    @JsonIgnore
+    public String accessorTransient;
+
+    @PrimaryDetailedView
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    @PrimaryDetailedView
+    public List<DefaultFilteringSubEntity> getDefaultEntities() {
+        return defaultEntities;
+    }
+
+    public void setDefaultEntities(final List<DefaultFilteringSubEntity> defaultEntities) {
+        this.defaultEntities = defaultEntities;
+    }
+
+    public List<OneFilteringSubEntity> getSubEntities() {
+        return subEntities;
+    }
+
+    public void setSubEntities(final List<OneFilteringSubEntity> subEntities) {
+        this.subEntities = subEntities;
+    }
+
+    @PrimaryDetailedView
+    public String getAccessor() {
+        return accessorTransient == null ? property + property : accessorTransient;
+    }
+
+    public void setAccessor(final String accessor) {
+        accessorTransient = accessor;
+    }
+
+    public FilteredClassEntity getFiltered() {
+        return filtered;
+    }
+
+    public void setFiltered(final FilteredClassEntity filtered) {
+        this.filtered = filtered;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringSubEntity.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringSubEntity.java
new file mode 100644
index 0000000..149ca05
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringSubEntity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.domain;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+
+/**
+* @author Michal Gajdos
+*/
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.ANY)
+@XmlAccessorType(XmlAccessType.PROPERTY)
+public class OneFilteringSubEntity {
+
+    public static final OneFilteringSubEntity INSTANCE;
+
+    static {
+        INSTANCE = new OneFilteringSubEntity();
+        INSTANCE.field1 = 20;
+        INSTANCE.field2 = 30;
+        INSTANCE.property1 = "property1";
+        INSTANCE.property2 = "property2";
+    }
+
+    @XmlElement
+    public int field1;
+
+    @XmlElement
+    @PrimaryDetailedView
+    public int field2;
+
+    private String property1;
+    private String property2;
+
+    @PrimaryDetailedView
+    public String getProperty1() {
+        return property1;
+    }
+
+    public void setProperty1(final String property1) {
+        this.property1 = property1;
+    }
+
+    public String getProperty2() {
+        return property2;
+    }
+
+    @PrimaryDetailedView
+    public void setProperty2(final String property2) {
+        this.property2 = property2;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEmptyEntityTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEmptyEntityTest.java
new file mode 100644
index 0000000..b2ee42c
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEmptyEntityTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.json;
+
+import java.util.Arrays;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.ext.ContextResolver;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.EmptyEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.NonEmptyEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+/**
+ * Use-cases to check whether empty class causes problems (JERSEY-2824 reproducer).
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JsonEmptyEntityTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    @Path("/")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class Resource {
+
+        @Path("nonEmptyEntity")
+        @GET
+        public NonEmptyEntity nonEmptyEntity() {
+            return NonEmptyEntity.INSTANCE;
+        }
+
+        @Path("emptyEntity")
+        @GET
+        public EmptyEntity emptyEntity() {
+            return new EmptyEntity();
+        }
+    }
+
+    public JsonEmptyEntityTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(Resource.class, EntityFilteringFeature.class)
+                .register(filteringProvider)
+                .register(new ContextResolver<ObjectMapper>() {
+                    @Override
+                    public ObjectMapper getContext(final Class<?> type) {
+                        return new ObjectMapper()
+                                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
+                                .setSerializationInclusion(JsonInclude.Include.NON_NULL);
+                    }
+                }));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Test
+    public void testNonEmptyEntity() throws Exception {
+        final NonEmptyEntity entity = target("nonEmptyEntity").request().get(NonEmptyEntity.class);
+
+        assertThat(entity.getValue(), is("foo"));
+        assertThat(entity.getEmptyEntity(), nullValue());
+    }
+
+    @Test
+    public void testEmptyEntity() throws Exception {
+        assertThat(target("emptyEntity").request().get(String.class), is("{}"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringClientTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringClientTest.java
new file mode 100644
index 0000000..9180c2e
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringClientTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.json;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.DefaultFilteringScope;
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.DefaultFilteringSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.FilteredClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringSubEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JsonEntityFilteringClientTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    @Parameterized.Parameter
+    public Class<Feature> filteringProvider;
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                // Resources.
+                .register(Resource.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(EntityFilteringFeature.class).register(filteringProvider);
+    }
+
+    @Path("/")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class Resource {
+
+        @POST
+        public String post(final String value) {
+            return value;
+        }
+    }
+
+    @Test
+    public void testEntityAnnotationsPrimaryView() throws Exception {
+        final OneFilteringOnClassEntity entity = target()
+                .request()
+                .post(Entity.entity(
+                                OneFilteringOnClassEntity.INSTANCE,
+                                MediaType.APPLICATION_JSON_TYPE,
+                                new Annotation[] {PrimaryDetailedView.Factory.get()}),
+                        OneFilteringOnClassEntity.class);
+
+        _testPrimaryViewEntity(entity);
+    }
+
+    @Test
+    public void testEntityAnnotationsDefaultView() throws Exception {
+        final OneFilteringOnClassEntity entity = target()
+                .request()
+                .post(Entity.entity(
+                                OneFilteringOnClassEntity.INSTANCE,
+                                MediaType.APPLICATION_JSON_TYPE,
+                                new Annotation[] {new DefaultFilteringScope()}),
+                        OneFilteringOnClassEntity.class);
+
+        _testEmptyEntity(entity);
+    }
+
+    @Test
+    public void testEntityAnnotationsInvalidView() throws Exception {
+        final OneFilteringOnClassEntity entity = target()
+                .request()
+                .post(Entity.entity(
+                                OneFilteringOnClassEntity.INSTANCE,
+                                MediaType.APPLICATION_JSON_TYPE,
+                                new Annotation[] {CustomAnnotationLiteral.INSTANCE}),
+                        OneFilteringOnClassEntity.class);
+
+        _testEmptyEntity(entity);
+    }
+
+    @Test
+    public void testConfigurationPrimaryView() throws Exception {
+        _testPrimaryViewEntity(retrieveEntity(PrimaryDetailedView.Factory.get()));
+    }
+
+    @Test
+    public void testConfigurationDefaultView() throws Exception {
+        _testEmptyEntity(retrieveEntity(new DefaultFilteringScope()));
+    }
+
+    @Test
+    public void testConfigurationMultipleViews() throws Exception {
+        _testPrimaryViewEntity(retrieveEntity(PrimaryDetailedView.Factory.get(), CustomAnnotationLiteral.INSTANCE));
+    }
+
+    private OneFilteringOnClassEntity retrieveEntity(final Annotation... annotations) {
+        final ClientConfig config = new ClientConfig()
+                .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, annotations.length == 1 ? annotations[0] : annotations);
+        configureClient(config);
+
+        return ClientBuilder.newClient(config)
+                .target(getBaseUri())
+                .request()
+                .post(Entity.entity(OneFilteringOnClassEntity.INSTANCE, MediaType.APPLICATION_JSON_TYPE),
+                        OneFilteringOnClassEntity.class);
+    }
+
+    @Test
+    public void testInvalidConfiguration() throws Exception {
+        final ClientConfig config = new ClientConfig()
+                .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, "invalid_value");
+        configureClient(config);
+
+        final OneFilteringOnClassEntity entity =
+                ClientBuilder.newClient(config)
+                        .target(getBaseUri())
+                        .request()
+                        .post(Entity.entity(OneFilteringOnClassEntity.INSTANCE, MediaType.APPLICATION_JSON_TYPE),
+                                OneFilteringOnClassEntity.class);
+
+        _testEmptyEntity(entity);
+    }
+
+    @Test
+    public void testEntityAnnotationsOverConfiguration() throws Exception {
+        final ClientConfig config = new ClientConfig()
+                .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, SecondaryDetailedView.Factory.get());
+        configureClient(config);
+
+        final ManyFilteringsOnClassEntity entity = ClientBuilder.newClient(config)
+                .target(getBaseUri())
+                .request()
+                .post(Entity.entity(
+                                ManyFilteringsOnClassEntity.INSTANCE,
+                                MediaType.APPLICATION_JSON_TYPE,
+                                new Annotation[] {PrimaryDetailedView.Factory.get()}),
+                        ManyFilteringsOnClassEntity.class);
+
+        // ManyFilteringsOnClassEntity
+        assertThat(entity.field, is(50));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(0));
+        assertThat(manyFilteringsSubEntity.getProperty1(), is("property1"));
+        assertThat(manyFilteringsSubEntity.getProperty2(), nullValue());
+    }
+
+    private void _testEmptyEntity(final OneFilteringOnClassEntity entity) {
+        // OneFilteringOnClassEntity
+        assertThat(entity.field, is(0));
+        assertThat(entity.accessorTransient, nullValue());
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), nullValue());
+    }
+
+    private void _testPrimaryViewEntity(final OneFilteringOnClassEntity entity) {
+        // OneFilteringOnClassEntity
+        assertThat(entity.field, is(10));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), notNullValue());
+        assertThat(entity.getDefaultEntities().size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.getDefaultEntities().get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), notNullValue());
+        assertThat(entity.getSubEntities().size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.getSubEntities().get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnClassTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnClassTest.java
new file mode 100644
index 0000000..e18eeee
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnClassTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.json;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.DefaultFilteringScope;
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.DefaultFilteringSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.FilteredClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringSubEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Use-cases with entity-filtering annotations on class, JSON output.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JsonEntityFilteringOnClassTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public JsonEntityFilteringOnClassTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(Resource.class, EntityFilteringFeature.class).register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Path("/")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class Resource {
+
+        @GET
+        @Path("OneFilteringEntity")
+        @PrimaryDetailedView
+        public OneFilteringOnClassEntity getOneFilteringEntity() {
+            return OneFilteringOnClassEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultView")
+        public OneFilteringOnClassEntity getOneFilteringEntityDefaultView() {
+            return OneFilteringOnClassEntity.INSTANCE;
+        }
+
+        @POST
+        @Path("OneFilteringEntity")
+        public String postOneFilteringEntity(final String value) {
+            return value;
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultViewResponse")
+        public Response getOneFilteringEntityDefaultViewResponse() {
+            return Response.ok().entity(OneFilteringOnClassEntity.INSTANCE, new Annotation[] {new DefaultFilteringScope()})
+                    .build();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityPrimaryView")
+        @PrimaryDetailedView
+        public ManyFilteringsOnClassEntity getManyFilteringsEntityPrimaryView() {
+            return ManyFilteringsOnClassEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("ManyFilteringsEntitySecondaryView")
+        @SecondaryDetailedView
+        public ManyFilteringsOnClassEntity getManyFilteringsEntitySecondaryView() {
+            return ManyFilteringsOnClassEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityDefaultView")
+        public ManyFilteringsOnClassEntity getManyFilteringsEntityDefaultView() {
+            return ManyFilteringsOnClassEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityManyViews")
+        @PrimaryDetailedView
+        @SecondaryDetailedView
+        public ManyFilteringsOnClassEntity getManyFilteringsEntityManyViews() {
+            return ManyFilteringsOnClassEntity.INSTANCE;
+        }
+    }
+
+    @Test
+    public void testOneEntityFilteringOnClass() throws Exception {
+        final OneFilteringOnClassEntity entity = target("OneFilteringEntity").request().get(OneFilteringOnClassEntity.class);
+
+        // OneFilteringOnClassEntity
+        assertThat(entity.field, is(10));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), notNullValue());
+        assertThat(entity.getDefaultEntities().size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.getDefaultEntities().get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), notNullValue());
+        assertThat(entity.getSubEntities().size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.getSubEntities().get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+    }
+
+    @Test
+    public void testOneEntityFilteringOnClassDefaultViewResponse() throws Exception {
+        final OneFilteringOnClassEntity entity = target("OneFilteringEntityDefaultViewResponse").request()
+                .get(OneFilteringOnClassEntity.class);
+
+        // OneFilteringOnClassEntity
+        assertThat(entity.field, is(0));
+        assertThat(entity.accessorTransient, nullValue());
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), nullValue());
+    }
+
+    @Test
+    public void testOneEntityFilteringOnClassDefaultView() throws Exception {
+        final OneFilteringOnClassEntity entity = target("OneFilteringEntityDefaultView").request()
+                .get(OneFilteringOnClassEntity.class);
+
+        // OneFilteringOnClassEntity
+        assertThat(entity.field, is(0));
+        assertThat(entity.accessorTransient, nullValue());
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), nullValue());
+    }
+
+    @Test
+    public void testMultipleViewsOnClass() throws Exception {
+        testOneEntityFilteringOnClass();
+        testOneEntityFilteringOnClassDefaultView();
+    }
+
+    @Test
+    public void testManyFilteringsEntityPrimaryView() throws Exception {
+        final ManyFilteringsOnClassEntity entity = target("ManyFilteringsEntityPrimaryView").request()
+                .get(ManyFilteringsOnClassEntity.class);
+
+        // ManyFilteringsOnClassEntity
+        assertThat(entity.field, is(50));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(0));
+        assertThat(manyFilteringsSubEntity.getProperty1(), is("property1"));
+        assertThat(manyFilteringsSubEntity.getProperty2(), nullValue());
+    }
+
+    @Test
+    public void testManyFilteringsEntitySecondaryView() throws Exception {
+        final ManyFilteringsOnClassEntity entity = target("ManyFilteringsEntitySecondaryView").request()
+                .get(ManyFilteringsOnClassEntity.class);
+
+        // ManyFilteringsOnClassEntity
+        assertThat(entity.field, is(50));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(0));
+        assertThat(oneFilteringSubEntity.getProperty1(), nullValue());
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(70));
+        assertThat(manyFilteringsSubEntity.getProperty1(), nullValue());
+        assertThat(manyFilteringsSubEntity.getProperty2(), is("property2"));
+    }
+
+    @Test
+    public void testManyFilteringsEntityDefaultView() throws Exception {
+        final ManyFilteringsOnClassEntity entity = target("ManyFilteringsEntityDefaultView").request()
+                .get(ManyFilteringsOnClassEntity.class);
+
+        // ManyFilteringsOnClassEntity
+        assertThat(entity.field, is(0));
+        assertThat(entity.accessorTransient, nullValue());
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, nullValue());
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, nullValue());
+    }
+
+    @Test
+    public void testManyFilteringsEntityManyViews() throws Exception {
+        final ManyFilteringsOnClassEntity entity = target("ManyFilteringsEntityManyViews").request()
+                .get(ManyFilteringsOnClassEntity.class);
+
+        // ManyFilteringsOnClassEntity
+        assertThat(entity.field, is(50));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(70));
+        assertThat(manyFilteringsSubEntity.getProperty1(), is("property1"));
+        assertThat(manyFilteringsSubEntity.getProperty2(), is("property2"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnPropertiesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnPropertiesTest.java
new file mode 100644
index 0000000..2d107b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnPropertiesTest.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.json;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.DefaultFilteringScope;
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.DefaultFilteringSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.FilteredClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnPropertiesEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringOnPropertiesEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringSubEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Use-cases with entity-filtering annotations on properties, JSON output.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JsonEntityFilteringOnPropertiesTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public JsonEntityFilteringOnPropertiesTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(Resource.class, EntityFilteringFeature.class).register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Path("/")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class Resource {
+
+        @GET
+        @Path("OneFilteringEntity")
+        @PrimaryDetailedView
+        public OneFilteringOnPropertiesEntity getOneFilteringEntity() {
+            return OneFilteringOnPropertiesEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultView")
+        public OneFilteringOnPropertiesEntity getOneFilteringEntityDefaultView() {
+            return OneFilteringOnPropertiesEntity.INSTANCE;
+        }
+
+        @POST
+        @Path("OneFilteringEntity")
+        public String postOneFilteringEntity(final String value) {
+            return value;
+        }
+
+        @GET
+        @Path("OneFilteringEntityDefaultViewResponse")
+        public Response getOneFilteringEntityDefaultViewResponse() {
+            return Response.ok().entity(OneFilteringOnPropertiesEntity.INSTANCE, new Annotation[] {new DefaultFilteringScope()})
+                    .build();
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityPrimaryView")
+        @PrimaryDetailedView
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntityPrimaryView() {
+            return ManyFilteringsOnPropertiesEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("ManyFilteringsEntitySecondaryView")
+        @SecondaryDetailedView
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntitySecondaryView() {
+            return ManyFilteringsOnPropertiesEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityDefaultView")
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntityDefaultView() {
+            return ManyFilteringsOnPropertiesEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("ManyFilteringsEntityManyViews")
+        @PrimaryDetailedView
+        @SecondaryDetailedView
+        public ManyFilteringsOnPropertiesEntity getManyFilteringsEntityManyViews() {
+            return ManyFilteringsOnPropertiesEntity.INSTANCE;
+        }
+    }
+
+    @Test
+    public void testOneEntityFilteringOnProperties() throws Exception {
+        final OneFilteringOnPropertiesEntity entity = target("OneFilteringEntity").request()
+                .get(OneFilteringOnPropertiesEntity.class);
+
+        // OneFilteringOnPropertiesEntity
+        assertThat(entity.field, is(80));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), notNullValue());
+        assertThat(entity.getDefaultEntities().size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.getDefaultEntities().get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), notNullValue());
+        assertThat(entity.getSubEntities().size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.getSubEntities().get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+    }
+
+    @Test
+    public void testOneEntityFilteringOnPropertiesDefaultViewResponse() throws Exception {
+        final OneFilteringOnPropertiesEntity entity = target("OneFilteringEntityDefaultViewResponse").request()
+                .get(OneFilteringOnPropertiesEntity.class);
+
+        // OneFilteringOnPropertiesEntity
+        assertThat(entity.field, is(80));
+        assertThat(entity.accessorTransient, nullValue());
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), nullValue());
+    }
+
+    @Test
+    public void testOneEntityFilteringOnPropertiesDefaultView() throws Exception {
+        final OneFilteringOnPropertiesEntity entity = target("OneFilteringEntityDefaultView").request()
+                .get(OneFilteringOnPropertiesEntity.class);
+
+        // OneFilteringOnPropertiesEntity
+        assertThat(entity.field, is(80));
+        assertThat(entity.accessorTransient, nullValue());
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.getFiltered();
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.getDefaultEntities(), nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.getSubEntities(), nullValue());
+    }
+
+    @Test
+    public void testMultipleViewsOnProperties() throws Exception {
+        testOneEntityFilteringOnProperties();
+        testOneEntityFilteringOnPropertiesDefaultView();
+    }
+
+    @Test
+    public void testManyFilteringsEntityPrimaryView() throws Exception {
+        final ManyFilteringsOnPropertiesEntity entity = target("ManyFilteringsEntityPrimaryView").request()
+                .get(ManyFilteringsOnPropertiesEntity.class);
+
+        // ManyFilteringsOnPropertiesEntity
+        assertThat(entity.field, is(90));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, nullValue());
+    }
+
+    @Test
+    public void testManyFilteringsEntitySecondaryView() throws Exception {
+        final ManyFilteringsOnPropertiesEntity entity = target("ManyFilteringsEntitySecondaryView").request()
+                .get(ManyFilteringsOnPropertiesEntity.class);
+
+        // ManyFilteringsOnPropertiesEntity
+        assertThat(entity.field, is(90));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(0));
+        assertThat(oneFilteringSubEntity.getProperty1(), nullValue());
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(70));
+        assertThat(manyFilteringsSubEntity.getProperty1(), nullValue());
+        assertThat(manyFilteringsSubEntity.getProperty2(), is("property2"));
+    }
+
+    @Test
+    public void testManyFilteringsEntityDefaultView() throws Exception {
+        final ManyFilteringsOnPropertiesEntity entity = target("ManyFilteringsEntityDefaultView").request()
+                .get(ManyFilteringsOnPropertiesEntity.class);
+
+        // ManyFilteringsOnPropertiesEntity
+        assertThat(entity.field, is(90));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), nullValue());
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, nullValue());
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, nullValue());
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, nullValue());
+    }
+
+    @Test
+    public void testManyFilteringsEntityManyViews() throws Exception {
+        final ManyFilteringsOnPropertiesEntity entity = target("ManyFilteringsEntityManyViews").request()
+                .get(ManyFilteringsOnPropertiesEntity.class);
+
+        // ManyFilteringsOnPropertiesEntity
+        assertThat(entity.field, is(90));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(70));
+        assertThat(manyFilteringsSubEntity.getProperty1(), is("property1"));
+        assertThat(manyFilteringsSubEntity.getProperty2(), is("property2"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringScopesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringScopesTest.java
new file mode 100644
index 0000000..4dba516
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringScopesTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.json;
+
+import java.util.Arrays;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Feature;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ComplexEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ComplexSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ComplexSubSubEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JsonEntityFilteringScopesTest extends JerseyTest {
+
+    @Parameterized.Parameters(name = "Provider: {0}")
+    public static Iterable<Class[]> providers() {
+        return Arrays.asList(new Class[][]{{MoxyJsonFeature.class}, {JacksonFeature.class}});
+    }
+
+    public JsonEntityFilteringScopesTest(final Class<Feature> filteringProvider) {
+        super(new ResourceConfig(Resource.class, EntityFilteringFeature.class).register(filteringProvider));
+
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Path("/")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public static class Resource {
+
+        @GET
+        @PrimaryDetailedView
+        public ComplexEntity get() {
+            return ComplexEntity.INSTANCE;
+        }
+    }
+
+    /**
+     * Primary -> Default -> Primary.
+     */
+    @Test
+    public void testEntityFilteringScopes() throws Exception {
+        final ComplexEntity entity = target().request().get(ComplexEntity.class);
+
+        // ComplexEntity
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // ComplexSubEntity
+        final ComplexSubEntity subEntity = entity.field;
+        assertThat(subEntity, notNullValue());
+        assertThat(subEntity.accessorTransient, is("fieldfield"));
+        assertThat(subEntity.field, is("field"));
+
+        // ComplexSubSubEntity
+        final ComplexSubSubEntity subSubEntity = entity.field.getProperty();
+        assertThat(subSubEntity.accessorTransient, is("fieldfield"));
+        assertThat(subSubEntity.getProperty(), is("property"));
+        assertThat(subSubEntity.field, nullValue());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringServerTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringServerTest.java
new file mode 100644
index 0000000..5ae7b7f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringServerTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2015, 2018 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.entity.filtering.json;
+
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.entity.filtering.DefaultFilteringScope;
+import org.glassfish.jersey.tests.e2e.entity.filtering.PrimaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.SecondaryDetailedView;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.DefaultFilteringSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.FilteredClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsOnClassEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.ManyFilteringsSubEntity;
+import org.glassfish.jersey.tests.e2e.entity.filtering.domain.OneFilteringSubEntity;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Suite;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        JsonEntityFilteringServerTest.ConfigurationServerTest.class,
+        JsonEntityFilteringServerTest.ConfigurationDefaultViewServerTest.class,
+        JsonEntityFilteringServerTest.AnnotationsServerTest.class,
+        JsonEntityFilteringServerTest.AnnotationsOverConfigurationServerTest.class
+})
+public class JsonEntityFilteringServerTest {
+
+    @Path("/")
+    @Produces("application/json")
+    public static class Resource {
+
+        @GET
+        @Path("configuration")
+        public ManyFilteringsOnClassEntity getConfiguration() {
+            return ManyFilteringsOnClassEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("configurationOverResource")
+        @SecondaryDetailedView
+        public ManyFilteringsOnClassEntity getConfigurationOverResource() {
+            return ManyFilteringsOnClassEntity.INSTANCE;
+        }
+
+        @GET
+        @Path("annotations")
+        public Response getAnnotations() {
+            return Response
+                    .ok()
+                    .entity(ManyFilteringsOnClassEntity.INSTANCE, new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+
+        @GET
+        @Path("annotationsOverConfiguration")
+        public Response getAnnotationsOverConfiguration() {
+            return Response
+                    .ok()
+                    .entity(ManyFilteringsOnClassEntity.INSTANCE, new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+
+        @GET
+        @Path("annotationsOverResource")
+        @SecondaryDetailedView
+        public Response getAnnotationsOverResource() {
+            return Response
+                    .ok()
+                    .entity(ManyFilteringsOnClassEntity.INSTANCE, new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+
+        @GET
+        @Path("annotationsOverConfigurationOverResource")
+        @SecondaryDetailedView
+        public Response getAnnotationsOverConfigurationOverResource() {
+            return Response
+                    .ok()
+                    .entity(ManyFilteringsOnClassEntity.INSTANCE, new Annotation[] {PrimaryDetailedView.Factory.get()})
+                    .build();
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class ConfigurationServerTest extends JerseyTest {
+
+        @Parameterized.Parameters(name = "Provider: {0}")
+        public static Iterable<Class[]> providers() {
+            return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+        }
+
+        public ConfigurationServerTest(final Class<Feature> filteringProvider) {
+            super(new ResourceConfig(Resource.class, EntityFilteringFeature.class)
+                    .register(filteringProvider)
+                    .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, PrimaryDetailedView.Factory.get()));
+
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+        }
+
+        @Test
+        public void testConfiguration() throws Exception {
+            _testEntity(target("configuration").request().get(ManyFilteringsOnClassEntity.class));
+        }
+
+        @Test
+        public void testConfigurationOverResource() throws Exception {
+            _testEntity(target("configurationOverResource").request().get(ManyFilteringsOnClassEntity.class));
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class ConfigurationDefaultViewServerTest extends JerseyTest {
+
+        @Parameterized.Parameters(name = "Provider: {0}")
+        public static Iterable<Class[]> providers() {
+            return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+        }
+
+        public ConfigurationDefaultViewServerTest(final Class<Feature> filteringProvider) {
+            super(new ResourceConfig(Resource.class, EntityFilteringFeature.class).register(filteringProvider));
+
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+        }
+
+        @Test
+        public void testConfiguration() throws Exception {
+            final ManyFilteringsOnClassEntity entity = target("configuration").request().get(ManyFilteringsOnClassEntity.class);
+
+            // ManyFilteringsOnClassEntity
+            assertThat(entity.field, is(0));
+            assertThat(entity.accessorTransient, nullValue());
+            assertThat(entity.getProperty(), nullValue());
+
+            // FilteredClassEntity
+            final FilteredClassEntity filtered = entity.filtered;
+            assertThat(filtered, nullValue());
+
+            // DefaultFilteringSubEntity
+            assertThat(entity.defaultEntities, nullValue());
+
+            // OneFilteringSubEntity
+            assertThat(entity.oneEntities, nullValue());
+
+            // ManyFilteringsSubEntity
+            assertThat(entity.manyEntities, nullValue());
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class AnnotationsServerTest extends JerseyTest {
+
+        @Parameterized.Parameters(name = "Provider: {0}")
+        public static Iterable<Class[]> providers() {
+            return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+        }
+
+        public AnnotationsServerTest(final Class<Feature> filteringProvider) {
+            super(new ResourceConfig(Resource.class, EntityFilteringFeature.class).register(filteringProvider));
+
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+        }
+
+        @Test
+        public void testAnnotations() throws Exception {
+            _testEntity(target("annotations").request().get(ManyFilteringsOnClassEntity.class));
+        }
+
+        @Test
+        public void testAnnotationsOverResource() throws Exception {
+            _testEntity(target("annotationsOverResource").request().get(ManyFilteringsOnClassEntity.class));
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class AnnotationsOverConfigurationServerTest extends JerseyTest {
+
+        @Parameterized.Parameters(name = "Provider: {0}")
+        public static Iterable<Class[]> providers() {
+            return Arrays.asList(new Class[][] {{MoxyJsonFeature.class}, {JacksonFeature.class}});
+        }
+
+        public AnnotationsOverConfigurationServerTest(final Class<Feature> filteringProvider) {
+            super(new ResourceConfig(Resource.class, EntityFilteringFeature.class)
+                    .register(filteringProvider)
+                    .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new DefaultFilteringScope()));
+
+            enable(TestProperties.DUMP_ENTITY);
+            enable(TestProperties.LOG_TRAFFIC);
+        }
+
+        @Test
+        public void testAnnotationsOverConfiguration() throws Exception {
+            _testEntity(target("annotationsOverConfiguration").request().get(ManyFilteringsOnClassEntity.class));
+        }
+
+        @Test
+        public void testAnnotationsOverConfigurationOverResource() throws Exception {
+            _testEntity(target("annotationsOverConfigurationOverResource").request().get(ManyFilteringsOnClassEntity.class));
+        }
+    }
+
+    private static void _testEntity(final ManyFilteringsOnClassEntity entity) {
+        // ManyFilteringsOnClassEntity
+        assertThat(entity.field, is(50));
+        assertThat(entity.accessorTransient, is("propertyproperty"));
+        assertThat(entity.getProperty(), is("property"));
+
+        // FilteredClassEntity
+        final FilteredClassEntity filtered = entity.filtered;
+        assertThat(filtered, notNullValue());
+        assertThat(filtered.field, is(0));
+        assertThat(filtered.getProperty(), nullValue());
+
+        // DefaultFilteringSubEntity
+        assertThat(entity.defaultEntities, notNullValue());
+        assertThat(entity.defaultEntities.size(), is(1));
+        final DefaultFilteringSubEntity defaultFilteringSubEntity = entity.defaultEntities.get(0);
+        assertThat(defaultFilteringSubEntity.field, is(true));
+        assertThat(defaultFilteringSubEntity.getProperty(), is(20L));
+
+        // OneFilteringSubEntity
+        assertThat(entity.oneEntities, notNullValue());
+        assertThat(entity.oneEntities.size(), is(1));
+        final OneFilteringSubEntity oneFilteringSubEntity = entity.oneEntities.get(0);
+        assertThat(oneFilteringSubEntity.field1, is(20));
+        assertThat(oneFilteringSubEntity.field2, is(30));
+        assertThat(oneFilteringSubEntity.getProperty1(), is("property1"));
+        assertThat(oneFilteringSubEntity.getProperty2(), is("property2"));
+
+        // ManyFilteringsSubEntity
+        assertThat(entity.manyEntities, notNullValue());
+        assertThat(entity.manyEntities.size(), is(1));
+        final ManyFilteringsSubEntity manyFilteringsSubEntity = entity.manyEntities.get(0);
+        assertThat(manyFilteringsSubEntity.field1, is(60));
+        assertThat(manyFilteringsSubEntity.field2, is(0));
+        assertThat(manyFilteringsSubEntity.getProperty1(), is("property1"));
+        assertThat(manyFilteringsSubEntity.getProperty2(), nullValue());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/MoxyEntityFilteringTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/MoxyEntityFilteringTest.java
new file mode 100644
index 0000000..aefabf4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/MoxyEntityFilteringTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2013, 2018 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.entity.filtering.json;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MoxyEntityFilteringTest extends JerseyTest {
+
+    @Path("/")
+    @Produces("application/json")
+    public static class Resource {
+
+        @GET
+        public XmlElementEntity getXmlAttributeEntity() {
+            return new XmlElementEntity(new XmlAttributeEntity("foo"));
+        }
+    }
+
+    @XmlRootElement
+    public static class XmlElementEntity {
+
+        @XmlElement
+        private XmlAttributeEntity value;
+
+        public XmlElementEntity() {
+        }
+
+        private XmlElementEntity(final XmlAttributeEntity value) {
+            this.value = value;
+        }
+
+        public XmlAttributeEntity getValue() {
+            return value;
+        }
+
+        public void setValue(final XmlAttributeEntity value) {
+            this.value = value;
+        }
+    }
+
+    @XmlRootElement
+    public static class XmlAttributeEntity {
+
+        @XmlAttribute
+        private String attribute;
+
+        public XmlAttributeEntity() {
+        }
+
+        public XmlAttributeEntity(final String attribute) {
+            this.attribute = attribute;
+        }
+
+        public String getAttribute() {
+            return attribute;
+        }
+
+        public void setAttribute(final String attribute) {
+            this.attribute = attribute;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(Resource.class)
+                // Features.
+                .register(EntityFilteringFeature.class);
+    }
+
+    @Test
+    public void testXmlAttributeEntity() throws Exception {
+        final XmlElementEntity entity = target().request().get(XmlElementEntity.class);
+
+        assertThat(entity, notNullValue());
+        assertThat(entity.getValue(), notNullValue());
+        assertThat(entity.getValue().getAttribute(), equalTo("foo"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/AbstractJsonTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/AbstractJsonTest.java
new file mode 100644
index 0000000..48c5afa
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/AbstractJsonTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.xml.bind.JAXBContext;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.jettison.JettisonConfig;
+import org.glassfish.jersey.jettison.JettisonJaxbContext;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.eclipse.persistence.jaxb.JAXBContextFactory;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Common functionality for JSON tests that are using multiple JSON providers (e.g. MOXy, Jackson, Jettison).
+ *
+ * @author Michal Gajdos
+ */
+public abstract class AbstractJsonTest extends JerseyTest {
+
+    private static final String PKG_NAME = "org/glassfish/jersey/tests/e2e/json/entity/";
+    private static final Logger LOGGER = Logger.getLogger(AbstractJsonTest.class.getName());
+
+    /**
+     * Helper class representing configuration for one test case.
+     */
+    protected static final class JsonTestSetup {
+
+        private final JsonTestProvider jsonProvider;
+        private final Class<?>[] testClasses;
+
+        protected JsonTestSetup(final Class<?> testClass, final JsonTestProvider jsonProvider) {
+            this(new Class<?>[] {testClass}, jsonProvider);
+        }
+
+        protected JsonTestSetup(final Class<?>[] testClasses, final JsonTestProvider jsonProvider) {
+            this.testClasses = testClasses;
+            this.jsonProvider = jsonProvider;
+        }
+
+        public JsonTestProvider getJsonProvider() {
+            return jsonProvider;
+        }
+
+        public Set<Object> getProviders() {
+            return jsonProvider.getProviders();
+        }
+
+        public Class<?> getEntityClass() {
+            return testClasses[0];
+        }
+
+        public Class<?>[] getTestClasses() {
+            return testClasses;
+        }
+
+        public Object getTestEntity() throws Exception {
+            return getEntityClass().getDeclaredMethod("createTestInstance").invoke(null);
+        }
+    }
+
+    @Provider
+    private static final class JAXBContextResolver implements ContextResolver<JAXBContext> {
+
+        private final JAXBContext context;
+        private final Set<Class<?>> types;
+
+        public JAXBContextResolver(final JettisonConfig jsonConfiguration, final Class<?>[] classes,
+                                   final boolean forMoxyProvider) throws Exception {
+            this.types = new HashSet<>(Arrays.asList(classes));
+
+            if (jsonConfiguration != null) {
+                this.context = new JettisonJaxbContext(jsonConfiguration, classes);
+            } else {
+                this.context = forMoxyProvider
+                        ? JAXBContextFactory.createContext(classes, new HashMap()) : JAXBContext.newInstance(classes);
+            }
+        }
+
+        @Override
+        public JAXBContext getContext(final Class<?> objectType) {
+            return (types.contains(objectType)) ? context : null;
+        }
+    }
+
+    private final JsonTestSetup jsonTestSetup;
+
+    /**
+     * Creates and configures a JAX-RS {@link Application} for given {@link JsonTestSetup}. The {@link Application} will
+     * contain one resource that can be accessed via {@code POST} method at {@code <jsonProviderName>/<entityClassName>} path.
+     * The resource also checks whether is the incoming JSON same as the one stored in a appropriate file.
+     *
+     * @param jsonTestSetup configuration to create a JAX-RS {@link Application} for.
+     * @return an {@link Application} instance.
+     */
+    private static Application configureJaxrsApplication(final JsonTestSetup jsonTestSetup) {
+        final Resource.Builder resourceBuilder = Resource.builder();
+
+        final String providerName = getProviderPathPart(jsonTestSetup);
+        final String testName = getEntityPathPart(jsonTestSetup);
+
+        resourceBuilder
+                .path("/" + providerName + "/" + testName)
+                .addMethod("POST")
+                .consumes(MediaType.APPLICATION_JSON_TYPE)
+                .produces(MediaType.APPLICATION_JSON_TYPE)
+                .handledBy(new Inflector<ContainerRequestContext, Response>() {
+
+                    @Override
+                    public Response apply(final ContainerRequestContext containerRequestContext) {
+                        final ContainerRequest containerRequest = (ContainerRequest) containerRequestContext;
+
+                        // Check if the JSON is the same as in the previous version.
+                        containerRequest.bufferEntity();
+                        try {
+                            String json = JsonTestHelper.getResourceAsString(PKG_NAME,
+                                    providerName + "_" + testName + (moxyJaxbProvider() || runningOnJdk7AndLater() ? "_MOXy" : "")
+                                            + ".json").trim();
+
+                            final InputStream entityStream = containerRequest.getEntityStream();
+                            String retrievedJson = JsonTestHelper.getEntityAsString(entityStream).trim();
+                            entityStream.reset();
+
+                            // JAXB-RI and MOXy generate namespace prefixes differently - unify them (ns1/ns2 into ns0)
+                            if (jsonTestSetup.getJsonProvider() instanceof JsonTestProvider.JettisonBadgerfishJsonTestProvider) {
+                                if (retrievedJson.contains("\"ns1\"")) {
+                                    json = json.replace("ns1", "ns0");
+                                    retrievedJson = retrievedJson.replace("ns1", "ns0");
+                                } else if (retrievedJson.contains("\"ns2\"")) {
+                                    json = json.replace("ns2", "ns0");
+                                    retrievedJson = retrievedJson.replace("ns2", "ns0");
+                                }
+                            }
+
+                            if (!json.equals(retrievedJson)) {
+                                LOGGER.log(Level.SEVERE, "Expected: " + json);
+                                LOGGER.log(Level.SEVERE, "Actual:   " + retrievedJson);
+
+                                return Response.ok("{\"error\":\"JSON values doesn't match.\"}").build();
+                            }
+                        } catch (final IOException e) {
+                            return Response.ok("{\"error\":\"Cannot find original JSON file.\"}").build();
+                        }
+
+                        final Object testBean = containerRequest.readEntity(jsonTestSetup.getEntityClass());
+                        return Response.ok(testBean).build();
+                    }
+
+                });
+
+        final ResourceConfig resourceConfig = new ResourceConfig()
+                .registerResources(resourceBuilder.build())
+                .register(jsonTestSetup.getJsonProvider().getFeature());
+
+        resourceConfig.registerInstances(getJaxbContextResolver(jsonTestSetup));
+
+        if (jsonTestSetup.getProviders() != null) {
+            resourceConfig.registerInstances(jsonTestSetup.getProviders());
+        }
+
+        return resourceConfig;
+    }
+
+    private static JAXBContextResolver getJaxbContextResolver(final JsonTestSetup jsonTestSetup) {
+        try {
+            return createJaxbContextResolver(jsonTestSetup.getJsonProvider(), jsonTestSetup.getTestClasses());
+        } catch (final Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static boolean runningOnJdk7AndLater() {
+        final String javaVersion = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("java.version"));
+        final int version = Integer.valueOf(javaVersion.split("\\.")[1]);
+
+        return version >= 7;
+    }
+
+    private static boolean moxyJaxbProvider() {
+        return "org.eclipse.persistence.jaxb.JAXBContextFactory".equals(
+                AccessController.doPrivileged(PropertiesHelper.getSystemProperty("javax.xml.bind.JAXBContext")));
+    }
+
+    /**
+     * Returns entity path part for given {@link JsonTestSetup} (based on the name of the entity).
+     *
+     * @return entity path part.
+     */
+    protected static String getEntityPathPart(final JsonTestSetup jsonTestSetup) {
+        return jsonTestSetup.getEntityClass().getSimpleName();
+    }
+
+    /**
+     * Creates new {@link ContextResolver} of {@link JAXBContext} instance for given {@link JsonTestProvider} and an entity
+     * class.
+     *
+     * @param jsonProvider provider to create a context resolver for.
+     * @param clazz        JAXB element class for JAXB context.
+     * @return an instance of JAXB context resolver.
+     * @throws Exception if the creation of {@code JAXBContextResolver} fails.
+     */
+    protected static JAXBContextResolver createJaxbContextResolver(final JsonTestProvider jsonProvider,
+                                                                   final Class<?> clazz) throws Exception {
+        return createJaxbContextResolver(jsonProvider, new Class<?>[] {clazz});
+    }
+
+    /**
+     * Creates new {@link ContextResolver} of {@link JAXBContext} instance for given {@link JsonTestProvider} and an entity
+     * classes.
+     *
+     * @param jsonProvider provider to create a context resolver for.
+     * @param classes      JAXB element classes for JAXB context.
+     * @return an instance of JAXB context resolver.
+     * @throws Exception if the creation of {@code JAXBContextResolver} fails.
+     */
+    protected static JAXBContextResolver createJaxbContextResolver(final JsonTestProvider jsonProvider, final Class<?>[] classes)
+            throws Exception {
+        return new JAXBContextResolver(jsonProvider.getConfiguration(), classes,
+                jsonProvider instanceof JsonTestProvider.MoxyJsonTestProvider);
+    }
+
+    protected AbstractJsonTest(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(configureJaxrsApplication(jsonTestSetup));
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        this.jsonTestSetup = jsonTestSetup;
+    }
+
+    /**
+     * Returns entity path part for current test case (based on the name of the entity).
+     *
+     * @return entity path part.
+     */
+    protected String getEntityPathPart() {
+        return getEntityPathPart(jsonTestSetup);
+    }
+
+    /**
+     * Returns provider path part for current test case (based on the name of the {@link JsonTestProvider}).
+     *
+     * @return provider path part.
+     */
+    protected String getProviderPathPart() {
+        return getProviderPathPart(jsonTestSetup);
+    }
+
+    /**
+     * Returns provider path part for given {@link JsonTestSetup} (based on the name of the {@link JsonTestProvider}).
+     *
+     * @return provider path part.
+     */
+    protected static String getProviderPathPart(final JsonTestSetup jsonTestSetup) {
+        return jsonTestSetup.jsonProvider.getClass().getSimpleName();
+    }
+
+    protected JsonTestSetup getJsonTestSetup() {
+        return jsonTestSetup;
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(getJsonTestSetup().getJsonProvider().getFeature());
+
+        config.register(getJaxbContextResolver(jsonTestSetup));
+
+        // Register additional providers.
+        if (getJsonTestSetup().getProviders() != null) {
+            for (final Object provider : getJsonTestSetup().getProviders()) {
+                config.register(provider);
+            }
+        }
+    }
+
+    @Test
+    public void test() throws Exception {
+        final Object entity = getJsonTestSetup().getTestEntity();
+
+        final Object receivedEntity = target()
+                .path(getProviderPathPart())
+                .path(getEntityPathPart())
+                .request("application/json; charset=UTF-8")
+                .post(Entity.entity(entity, "application/json; charset=UTF-8"), getJsonTestSetup().getEntityClass());
+
+        // Print out configuration for this test case as there is no way to rename generated JUnit tests at the moment.
+        // TODO remove once JUnit supports parameterized tests with custom names
+        // TODO (see http://stackoverflow.com/questions/650894/change-test-name-of-parameterized-tests
+        // TODO or https://github.com/KentBeck/junit/pull/393)
+        assertEquals(String.format("%s - %s: Received JSON entity content does not match expected JSON entity content.",
+                getJsonTestSetup().getJsonProvider().getClass().getSimpleName(),
+                getJsonTestSetup().getEntityClass().getSimpleName()), entity, receivedEntity);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/GenericTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/GenericTest.java
new file mode 100644
index 0000000..5f7f5b9
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/GenericTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.glassfish.jersey.tests.e2e.json.entity.ColorHolder;
+import org.glassfish.jersey.tests.e2e.json.entity.Jersey1199List;
+
+import org.junit.Ignore;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Unignore when you need to run a specific test.
+ *
+ * @author Michal Gajdos
+ */
+@Ignore("Unignore only when you need to run a specific test.")
+@RunWith(Parameterized.class)
+public class GenericTest extends AbstractJsonTest {
+
+    @Parameterized.Parameters()
+    public static Collection<JsonTestSetup[]> generateTestCases() throws Exception {
+        final List<JsonTestSetup[]> jsonTestSetups = new LinkedList<JsonTestSetup[]>();
+        final Class<?>[] classes = {Jersey1199List.class, ColorHolder.class};
+
+        for (JsonTestProvider jsonProvider : new JsonTestProvider[]{new JsonTestProvider.MoxyJsonTestProvider()}) {
+            jsonTestSetups.add(new JsonTestSetup[]{new JsonTestSetup(classes, jsonProvider)});
+        }
+
+        return jsonTestSetups;
+    }
+
+    public GenericTest(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(jsonTestSetup);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/InheritanceTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/InheritanceTest.java
new file mode 100644
index 0000000..881d15d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/InheritanceTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.glassfish.jersey.tests.e2e.json.entity.Animal;
+import org.glassfish.jersey.tests.e2e.json.entity.AnimalList;
+import org.glassfish.jersey.tests.e2e.json.entity.Cat;
+import org.glassfish.jersey.tests.e2e.json.entity.Dog;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class InheritanceTest extends AbstractJsonTest {
+
+    @Parameterized.Parameters()
+    public static Collection<JsonTestSetup[]> generateTestCases() throws Exception {
+        final List<JsonTestSetup[]> jsonTestSetups = new LinkedList<JsonTestSetup[]>();
+        final Class<?>[] classes = {AnimalList.class, Animal.class, Dog.class, Cat.class};
+
+        for (final JsonTestProvider jsonProvider : JsonTestProvider.JAXB_PROVIDERS) {
+            // TODO - remove the condition after jsonb polymorphic adapter is implemented
+            if (!(jsonProvider instanceof JsonTestProvider.JsonbTestProvider)) {
+                jsonTestSetups.add(new JsonTestSetup[]{new JsonTestSetup(classes, jsonProvider)});
+            }
+        }
+
+        return jsonTestSetups;
+    }
+
+    public InheritanceTest(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(jsonTestSetup);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jackson1JsonViewTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jackson1JsonViewTest.java
new file mode 100644
index 0000000..d71dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jackson1JsonViewTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2014, 2018 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.json;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson1.Jackson1Feature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.codehaus.jackson.map.annotate.JsonView;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer for JERSEY-1878.
+ *
+ * @author Michal Gajdos
+ */
+public class Jackson1JsonViewTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class, Jackson1Feature.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(Jackson1Feature.class);
+    }
+
+    public static class SimpleView {}
+    public static class DetailedView {}
+
+    public static class TestEntity {
+
+        public static final TestEntity ENTITY = new TestEntity("simple", "detailed");
+        public static final TestEntity DETAILED = new TestEntity(null, "detailed");
+
+        private String simple;
+        private String detailed;
+
+        public TestEntity() {
+        }
+
+        public TestEntity(final String simple, final String detailed) {
+            this.simple = simple;
+            this.detailed = detailed;
+        }
+
+        @JsonView(SimpleView.class)
+        public String getSimple() {
+            return simple;
+        }
+
+        @JsonView(DetailedView.class)
+        public String getDetailed() {
+            return detailed;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            final TestEntity that = (TestEntity) o;
+
+            if (detailed != null ? !detailed.equals(that.detailed) : that.detailed != null) {
+                return false;
+            }
+            if (simple != null ? !simple.equals(that.simple) : that.simple != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = simple != null ? simple.hashCode() : 0;
+            result = 31 * result + (detailed != null ? detailed.hashCode() : 0);
+            return result;
+        }
+    }
+
+    @Path("/")
+    @Singleton
+    public static class MyResource {
+
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        @Path("async")
+        @JsonView(DetailedView.class)
+        public void getAsync(@Suspended final AsyncResponse response) {
+            response.resume(TestEntity.ENTITY);
+        }
+
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        @Path("sync")
+        @JsonView(DetailedView.class)
+        public TestEntity getSync() {
+            return TestEntity.ENTITY;
+        }
+    }
+
+    @Test
+    public void testSync() throws Exception {
+        final Response response = target().path("sync").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(TestEntity.class), is(TestEntity.DETAILED));
+    }
+
+    @Test
+    public void testAsync() throws Exception {
+        final Response response = target().path("async").request().async().get().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(TestEntity.class), is(TestEntity.DETAILED));
+
+        response.close();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JacksonJsonViewTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JacksonJsonViewTest.java
new file mode 100644
index 0000000..c4c8dd9
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JacksonJsonViewTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013, 2018 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.json;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.fasterxml.jackson.annotation.JsonView;
+
+/**
+ * Reproducer for JERSEY-1878.
+ *
+ * @author Michal Gajdos
+ */
+public class JacksonJsonViewTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class, JacksonFeature.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(JacksonFeature.class);
+    }
+
+    public static class SimpleView {}
+    public static class DetailedView {}
+
+    public static class TestEntity {
+
+        public static final TestEntity ENTITY = new TestEntity("simple", "detailed");
+        public static final TestEntity DETAILED = new TestEntity(null, "detailed");
+
+        private String simple;
+        private String detailed;
+
+        public TestEntity() {
+        }
+
+        public TestEntity(final String simple, final String detailed) {
+            this.simple = simple;
+            this.detailed = detailed;
+        }
+
+        @JsonView(SimpleView.class)
+        public String getSimple() {
+            return simple;
+        }
+
+        @JsonView(DetailedView.class)
+        public String getDetailed() {
+            return detailed;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            final TestEntity that = (TestEntity) o;
+
+            if (detailed != null ? !detailed.equals(that.detailed) : that.detailed != null) {
+                return false;
+            }
+            if (simple != null ? !simple.equals(that.simple) : that.simple != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = simple != null ? simple.hashCode() : 0;
+            result = 31 * result + (detailed != null ? detailed.hashCode() : 0);
+            return result;
+        }
+    }
+
+    @Path("/")
+    @Singleton
+    public static class MyResource {
+
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        @Path("async")
+        @JsonView(DetailedView.class)
+        public void getAsync(@Suspended final AsyncResponse response) {
+            response.resume(TestEntity.ENTITY);
+        }
+
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        @Path("sync")
+        @JsonView(DetailedView.class)
+        public TestEntity getSync() {
+            return TestEntity.ENTITY;
+        }
+    }
+
+    @Test
+    public void testSync() throws Exception {
+        final Response response = target().path("sync").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(TestEntity.class), is(TestEntity.DETAILED));
+    }
+
+    @Test
+    public void testAsync() throws Exception {
+        final Response response = target().path("async").request().async().get().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(TestEntity.class), is(TestEntity.DETAILED));
+
+        response.close();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JaxbTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JaxbTest.java
new file mode 100644
index 0000000..a1500d9
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JaxbTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.glassfish.jersey.tests.e2e.json.entity.AnotherArrayTestBean;
+import org.glassfish.jersey.tests.e2e.json.entity.AttrAndCharDataBean;
+import org.glassfish.jersey.tests.e2e.json.entity.ComplexBeanWithAttributes;
+import org.glassfish.jersey.tests.e2e.json.entity.ComplexBeanWithAttributes2;
+import org.glassfish.jersey.tests.e2e.json.entity.ComplexBeanWithAttributes3;
+import org.glassfish.jersey.tests.e2e.json.entity.ComplexBeanWithAttributes4;
+import org.glassfish.jersey.tests.e2e.json.entity.EmptyElementBean;
+import org.glassfish.jersey.tests.e2e.json.entity.EmptyElementContainingBean;
+import org.glassfish.jersey.tests.e2e.json.entity.EncodedContentBean;
+import org.glassfish.jersey.tests.e2e.json.entity.FakeArrayBean;
+import org.glassfish.jersey.tests.e2e.json.entity.IntArray;
+import org.glassfish.jersey.tests.e2e.json.entity.ListAndNonListBean;
+import org.glassfish.jersey.tests.e2e.json.entity.ListEmptyBean;
+import org.glassfish.jersey.tests.e2e.json.entity.ListWrapperBean;
+import org.glassfish.jersey.tests.e2e.json.entity.MyResponse;
+import org.glassfish.jersey.tests.e2e.json.entity.NamespaceBean;
+import org.glassfish.jersey.tests.e2e.json.entity.NamespaceBeanWithAttribute;
+import org.glassfish.jersey.tests.e2e.json.entity.NullStringBean;
+import org.glassfish.jersey.tests.e2e.json.entity.Person;
+import org.glassfish.jersey.tests.e2e.json.entity.PureCharDataBean;
+import org.glassfish.jersey.tests.e2e.json.entity.RegisterMessage;
+import org.glassfish.jersey.tests.e2e.json.entity.SimpleBean;
+import org.glassfish.jersey.tests.e2e.json.entity.SimpleBeanWithAttributes;
+import org.glassfish.jersey.tests.e2e.json.entity.SimpleBeanWithJustOneAttribute;
+import org.glassfish.jersey.tests.e2e.json.entity.SimpleBeanWithJustOneAttributeAndValue;
+import org.glassfish.jersey.tests.e2e.json.entity.TreeModel;
+import org.glassfish.jersey.tests.e2e.json.entity.TwoListsWrapperBean;
+import org.glassfish.jersey.tests.e2e.json.entity.User;
+import org.glassfish.jersey.tests.e2e.json.entity.UserTable;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JaxbTest extends AbstractJsonTest {
+
+    private static final Class<?>[] CLASSES = {
+            AnotherArrayTestBean.class,
+            AttrAndCharDataBean.class,
+            ComplexBeanWithAttributes.class,
+            ComplexBeanWithAttributes2.class,
+            ComplexBeanWithAttributes3.class,
+            ComplexBeanWithAttributes4.class,
+            EmptyElementBean.class,
+            EmptyElementContainingBean.class,
+            EncodedContentBean.class,
+            FakeArrayBean.class,
+            IntArray.class,
+            ListAndNonListBean.class,
+            ListEmptyBean.class,
+            ListWrapperBean.class,
+            MyResponse.class,
+            NamespaceBean.class,
+            NamespaceBeanWithAttribute.class,
+            NullStringBean.class,
+            Person.class,
+            PureCharDataBean.class,
+            RegisterMessage.class,
+            SimpleBean.class,
+            SimpleBeanWithAttributes.class,
+            SimpleBeanWithJustOneAttribute.class,
+            SimpleBeanWithJustOneAttributeAndValue.class,
+            TreeModel.class,
+            TwoListsWrapperBean.class,
+            User.class,
+            UserTable.class
+    };
+
+    public JaxbTest(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(jsonTestSetup);
+    }
+
+    @Parameterized.Parameters()
+    public static Collection<JsonTestSetup[]> getJsonProviders() throws Exception {
+        final List<JsonTestSetup[]> jsonTestSetups = new LinkedList<>();
+
+        for (final JsonTestProvider jsonProvider : JsonTestProvider.JAXB_PROVIDERS) {
+            for (final Class<?> entityClass : CLASSES) {
+                // TODO - remove the condition after jsonb polymorphic adapter is implemented
+                if (!(jsonProvider instanceof JsonTestProvider.JsonbTestProvider)) {
+                    jsonTestSetups.add(new JsonTestSetup[]{new JsonTestSetup(entityClass, jsonProvider)});
+                }
+            }
+        }
+
+        return jsonTestSetups;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1199Test.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1199Test.java
new file mode 100644
index 0000000..b3dec8d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1199Test.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.json.bind.adapter.JsonbAdapter;
+
+import org.glassfish.jersey.tests.e2e.json.entity.ColorHolder;
+import org.glassfish.jersey.tests.e2e.json.entity.Jersey1199List;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class Jersey1199Test extends AbstractJsonTest {
+
+    @Parameterized.Parameters()
+    public static Collection<JsonTestSetup[]> generateTestCases() throws Exception {
+        final List<JsonTestSetup[]> jsonTestSetups = new LinkedList<>();
+        final Class<?>[] classes = {Jersey1199List.class, ColorHolder.class};
+
+        for (final JsonTestProvider jsonProvider : JsonTestProvider.JAXB_PROVIDERS) {
+            jsonTestSetups.add(new JsonTestSetup[]{new JsonTestSetup(classes, jsonProvider)});
+        }
+
+        return jsonTestSetups;
+    }
+
+    public Jersey1199Test(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(jsonTestSetup);
+    }
+
+    /**
+     * Custom {@link JsonbAdapter} to provide JSONB with the type hidden behind the generic Object array returned by
+     * {@link Jersey1199List#getObjects()}
+     */
+    public static class JsonbObjectToColorHolderAdapter implements JsonbAdapter<Object[], ColorHolder[]> {
+
+        @Override
+        public ColorHolder[] adaptToJson(Object[] o) throws Exception {
+            return Arrays.copyOf(o, o.length, ColorHolder[].class);
+        }
+
+        @Override
+        public Object[] adaptFromJson(ColorHolder[] colorHolder) throws Exception {
+            return colorHolder;
+        }
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1835Test.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1835Test.java
new file mode 100644
index 0000000..c472aa6
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1835Test.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.json;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.glassfish.jersey.tests.e2e.json.entity.SingleItemListWrapperBean;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Reproducible test case for JERSEY-1835.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class Jersey1835Test extends AbstractJsonTest {
+
+    @Parameterized.Parameters()
+    public static Collection<JsonTestSetup[]> generateTestCases() throws Exception {
+        final List<JsonTestSetup[]> result = new LinkedList<JsonTestSetup[]>();
+        result.add(new JsonTestSetup[] {new JsonTestSetup(new Class<?>[] {SingleItemListWrapperBean.class},
+                new JsonTestProvider.JettisonMappedJsonTestProvider())});
+        return result;
+    }
+
+    public Jersey1835Test(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(jsonTestSetup);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingDisabledTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingDisabledTest.java
new file mode 100644
index 0000000..57a4e66
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingDisabledTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, 2018 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.json;
+
+import java.io.StringReader;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+
+import org.glassfish.jersey.internal.InternalProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JsonProcessingDisabledTest extends JerseyTest {
+
+    private static final String JSON_OBJECT_STR = "{\"foo\":\"bar\"}";
+    private static final JsonObject JSON_OBJECT = Json.createReader(new StringReader(JSON_OBJECT_STR)).readObject();
+
+    @Path("/")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public static class Resource {
+
+        @POST
+        @Path("jsonObject")
+        public JsonObject postJsonObject(final JsonObject jsonObject) {
+            return jsonObject;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(Resource.class)
+                .property(ServerProperties.JSON_PROCESSING_FEATURE_DISABLE, true)
+                // Make sure other JSON providers are disabled as well.
+                .property(InternalProperties.JSON_FEATURE_SERVER, "JsonProcessing");
+    }
+
+    @Test
+    public void testJsonObject() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_OBJECT));
+
+        assertEquals(415, response.getStatus());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingTest.java
new file mode 100644
index 0000000..ab9f523
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2013, 2018 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.json;
+
+import java.io.StringReader;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+
+import org.glassfish.jersey.jsonp.JsonProcessingFeature;
+import org.glassfish.jersey.server.JSONP;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+// @RunWith(ConcurrentRunner.class)
+public class JsonProcessingTest extends JerseyTest {
+
+    private static final String JSON_OBJECT_STR1 = "{\"foo\":\"bar\"}";
+    private static final String JSON_OBJECT_STR2 = "{\"foo\": 12345}";
+    private static final String JSON_ARRAY_STR1 = "[" + JSON_OBJECT_STR1 + "," + JSON_OBJECT_STR1 + "]";
+    private static final String JSON_ARRAY_STR2 = "[" + JSON_OBJECT_STR2 + "," + JSON_OBJECT_STR2 + "]";
+    private static final String JSON_ARRAY_VALUE_STR = "[null]";
+
+    private static final JsonObject JSON_OBJECT = Json.createReader(new StringReader(JSON_OBJECT_STR1)).readObject();
+    private static final JsonArray JSON_ARRAY = Json.createReader(new StringReader(JSON_ARRAY_STR1)).readArray();
+    private static final JsonArray JSON_ARRAY_VALUE = Json.createReader(new StringReader(JSON_ARRAY_VALUE_STR))
+                                                          .readArray();
+
+    private static final JsonValue JSON_VALUE_BOOL = JsonValue.TRUE;
+    private static final JsonString JSON_VALUE_STRING = Json.createReader(
+            new StringReader(JSON_ARRAY_STR1)).readArray().getJsonObject(0).getJsonString("foo");
+    private static final JsonNumber JSON_VALUE_NUMBER = Json.createReader(
+            new StringReader(JSON_ARRAY_STR2)).readArray().getJsonObject(0).getJsonNumber("foo");
+
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        @Path("jsonObject")
+        public JsonObject postJsonObject(final JsonObject jsonObject) {
+            return jsonObject;
+        }
+
+        @POST
+        @Path("jsonStructure")
+        public JsonStructure postJsonStructure(final JsonStructure jsonStructure) {
+            return jsonStructure;
+        }
+
+        @POST
+        @Path("jsonArray")
+        public JsonArray postJsonArray(final JsonArray jsonArray) {
+            return jsonArray;
+        }
+
+        @POST
+        @Path("jsonValue")
+        public JsonValue postJsonValue(final JsonValue jsonValue) {
+            return jsonValue;
+        }
+
+        @POST
+        @Path("jsonString")
+        public JsonString postJsonString(final JsonString jsonString) {
+            return jsonString;
+        }
+
+        @POST
+        @Path("jsonNumber")
+        public JsonValue postJsonNumber(final JsonNumber jsonNumber) {
+            return jsonNumber;
+        }
+
+        @GET
+        @JSONP
+        @Path("jsonObjectWithPadding")
+        @Produces("application/javascript")
+        public JsonObject getJsonObjectWithPadding() {
+            return JSON_OBJECT;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(Resource.class)
+                // Make sure to disable auto-discovery (MOXy, BeanValidation, ...) and register ValidationFeature.
+                .property(ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true)
+                .register(JsonProcessingFeature.class);
+    }
+
+    @Test
+    public void testJsonObject() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_OBJECT));
+
+        assertEquals(JSON_OBJECT, response.readEntity(JsonObject.class));
+    }
+
+    @Test
+    public void testJsonObjectAsString() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON)
+                                                      .post(Entity.json(JSON_OBJECT_STR1));
+
+        assertEquals(JSON_OBJECT, response.readEntity(JsonObject.class));
+    }
+
+    @Test
+    public void testJsonObjectPlus() throws Exception {
+        final Response response = target("jsonObject").request("application/foo+json").post(Entity.json(JSON_OBJECT));
+
+        assertEquals(JSON_OBJECT, response.readEntity(JsonObject.class));
+    }
+
+    @Test
+    public void testJsonObjectAsStringPlus() throws Exception {
+        final Response response = target("jsonObject").request("application/foo+json")
+                                                      .post(Entity.json(JSON_OBJECT_STR1));
+
+        assertEquals(JSON_OBJECT, response.readEntity(JsonObject.class));
+    }
+
+    @Test
+    public void testJsonObjectWrongTarget() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_OBJECT));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonObjectAsStringWrongTarget() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json(JSON_OBJECT_STR1));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonObjectWrongEntity() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_ARRAY));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonObjectAsStringWrongEntity() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON)
+                                                      .post(Entity.json(JSON_ARRAY_STR1));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonObjectWrongMediaType() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_OCTET_STREAM).post(Entity.json(JSON_OBJECT));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonObjectAsStringWrongMediaType() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_OCTET_STREAM)
+                                                      .post(Entity.json(JSON_OBJECT_STR1));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArray() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_ARRAY));
+
+        assertEquals(JSON_ARRAY, response.readEntity(JsonArray.class));
+    }
+
+    @Test
+    public void testJsonArrayAsString() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json(JSON_ARRAY_STR1));
+
+        assertEquals(JSON_ARRAY, response.readEntity(JsonArray.class));
+    }
+
+    @Test
+    public void testJsonArrayPlus() throws Exception {
+        final Response response = target("jsonArray").request("application/foo+json").post(Entity.json(JSON_ARRAY));
+
+        assertEquals(JSON_ARRAY, response.readEntity(JsonArray.class));
+    }
+
+    @Test
+    public void testJsonArrayAsStringPlus() throws Exception {
+        final Response response = target("jsonArray").request("application/foo+json")
+                                                     .post(Entity.json(JSON_ARRAY_STR1));
+
+        assertEquals(JSON_ARRAY, response.readEntity(JsonArray.class));
+    }
+
+    @Test
+    public void testJsonArrayWrongTarget() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_ARRAY));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArrayAsStringWrongTarget() throws Exception {
+        final Response response = target("jsonObject").request(MediaType.APPLICATION_JSON)
+                                                      .post(Entity.json(JSON_ARRAY_STR1));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArrayWrongEntity() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_OBJECT));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArrayAsStringWrongEntity() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json(JSON_OBJECT_STR1));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArrayWrongMediaType() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_OCTET_STREAM).post(Entity.json(JSON_ARRAY));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArraytAsStringWrongMediaType() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_OCTET_STREAM)
+                                                     .post(Entity.json(JSON_ARRAY_STR1));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testJsonArrayValueEntity() throws Exception {
+        final Response response = target("jsonArray").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_ARRAY_VALUE));
+
+        assertEquals(JSON_ARRAY_VALUE, response.readEntity(JsonArray.class));
+    }
+
+    @Test
+    public void testJsonStructureArray() throws Exception {
+        final Response response = target("jsonStructure").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_ARRAY));
+
+        assertEquals(JSON_ARRAY, response.readEntity(JsonStructure.class));
+    }
+
+    @Test
+    public void testJsonStructureObject() throws Exception {
+        final Response response = target("jsonStructure").request(MediaType.APPLICATION_JSON).post(Entity.json(JSON_OBJECT));
+
+        assertEquals(JSON_OBJECT, response.readEntity(JsonStructure.class));
+    }
+
+    @Test
+    public void testJsonValueBool() throws Exception {
+        final Response response = target("jsonValue").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json(JSON_VALUE_BOOL));
+
+        assertEquals(JSON_VALUE_BOOL, response.readEntity(JsonValue.class));
+    }
+
+    @Test
+    public void testJsonValueString() throws Exception {
+        final Response response = target("jsonString").request(MediaType.APPLICATION_JSON)
+                                                      .post(Entity.json(JSON_VALUE_STRING));
+
+        assertEquals(JSON_VALUE_STRING, response.readEntity(JsonString.class));
+    }
+
+    @Test
+    public void testJsonValueStringAsValue() throws Exception {
+        final Response response = target("jsonValue").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json(JSON_VALUE_STRING));
+
+        assertEquals(JSON_VALUE_STRING, response.readEntity(JsonString.class));
+    }
+
+    @Test
+    public void testJsonValueStringAsString() throws Exception {
+        final Response response = target("jsonValue").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json("\"Red 5\""));
+
+        assertEquals("Red 5", response.readEntity(JsonString.class).getString());
+    }
+
+    @Test
+    public void testJsonValueNumber() throws Exception {
+        final Response response = target("jsonNumber").request(MediaType.APPLICATION_JSON)
+                                                      .post(Entity.json(JSON_VALUE_NUMBER));
+
+        assertEquals(JSON_VALUE_NUMBER, response.readEntity(JsonNumber.class));
+    }
+
+    @Test
+    public void testJsonValueNumberAsValue() throws Exception {
+        final Response response = target("jsonValue").request(MediaType.APPLICATION_JSON)
+                                                     .post(Entity.json(JSON_VALUE_NUMBER));
+
+        assertEquals(JSON_VALUE_NUMBER, response.readEntity(JsonNumber.class));
+    }
+
+    @Test
+    public void testJsonObjectWithPadding() throws Exception {
+        final Response response = target("jsonObjectWithPadding").request("application/javascript").get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(JSONP.DEFAULT_CALLBACK + "(" + JSON_OBJECT_STR1 + ")"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestHelper.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestHelper.java
new file mode 100644
index 0000000..1706ceb
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestHelper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010, 2018 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.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+public class JsonTestHelper {
+
+    public static String getResourceAsString(String prefix, String resource) throws IOException {
+        return getEntityAsString(Thread.currentThread().getContextClassLoader().getResourceAsStream(prefix + resource));
+    }
+
+    public static String getEntityAsString(InputStream inputStream) throws IOException {
+        Reader reader = new InputStreamReader(inputStream);
+        StringBuilder sb = new StringBuilder();
+        char[] c = new char[1024];
+        int l;
+        while ((l = reader.read(c)) != -1) {
+            sb.append(c, 0, l);
+        }
+        return sb.toString();
+    }
+
+    public static <T> T createTestInstance(Class<T> clazz) {
+        try {
+            Method createMethod = clazz.getDeclaredMethod("createTestInstance");
+            //noinspection unchecked
+            return (T) createMethod.invoke(clazz);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    public static boolean isCollectionEmpty(final Collection<?> collection) {
+        return collection == null || collection.isEmpty();
+    }
+
+    public static <T> boolean areCollectionsEqual(final Collection<T> collection1, final Collection<T> collection2) {
+        return collection1 == collection2
+                || (isCollectionEmpty(collection1) && isCollectionEmpty(collection2))
+                || (collection1 != null && collection1.equals(collection2));
+    }
+
+    public static boolean isArrayEmpty(final Object[] array) {
+        return array == null || array.length == 0;
+    }
+
+    public static <T> boolean areArraysEqual(final T[] array1, final T[] array2) {
+        return array1 == array2
+                || (isArrayEmpty(array1) && isArrayEmpty(array2))
+                || Arrays.equals(array1, array2);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestProvider.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestProvider.java
new file mode 100644
index 0000000..3c72b60
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestProvider.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2013, 2018 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.json;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.jackson1.Jackson1Feature;
+import org.glassfish.jersey.jettison.JettisonConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.jsonb.JsonBindingFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+
+/**
+ * Common class for JSON providers that should be used for testing JSON capabilities.
+ *
+ * @author Michal Gajdos
+ */
+public abstract class JsonTestProvider {
+
+    public static final Collection<JsonTestProvider> JAXB_PROVIDERS = new LinkedHashSet<JsonTestProvider>() {{
+        add(new JacksonJsonTestProvider());
+        add(new Jackson1JsonTestProvider());
+        add(new JettisonMappedJsonTestProvider());
+        add(new JettisonBadgerfishJsonTestProvider());
+        add(new MoxyJsonTestProvider());
+        add(new JsonbTestProvider());
+    }};
+
+    //  TODO add MoxyJsonTestProvider once MOXy supports POJO
+    public static final Collection<JsonTestProvider> POJO_PROVIDERS = new LinkedHashSet<JsonTestProvider>() {{
+        add(new JacksonJsonTestProvider());
+        add(new Jackson1JsonTestProvider());
+    }};
+
+    private Feature feature;
+    private JettisonConfig configuration;
+    private Set<Object> providers = new LinkedHashSet<>();
+
+    public static class JettisonMappedJsonTestProvider extends JsonTestProvider {
+
+        public JettisonMappedJsonTestProvider() {
+            final JettisonConfig jsonConfiguration =
+                    JettisonConfig.mappedJettison().xml2JsonNs(new HashMap<String,
+                            String>() {{
+                        put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+                        put("http://example.com", "example");
+                        put("http://test.jaxb.com", "jaxb");
+                    }}).serializeAsArray("singleItemList").build();
+
+            setFeature(new JettisonFeature());
+            setConfiguration(jsonConfiguration);
+        }
+
+    }
+
+    public static class JettisonBadgerfishJsonTestProvider extends JsonTestProvider {
+
+        public JettisonBadgerfishJsonTestProvider() {
+            setFeature(new JettisonFeature());
+
+            setConfiguration(JettisonConfig.badgerFish().build());
+        }
+
+    }
+
+    public static class MoxyJsonTestProvider extends JsonTestProvider {
+
+        public MoxyJsonTestProvider() {
+            setFeature(new MoxyJsonFeature());
+            getProviders().add(new MoxyJsonConfigurationContextResolver());
+        }
+
+    }
+
+    @Provider
+    protected static final class MoxyJsonConfigurationContextResolver implements ContextResolver<MoxyJsonConfig> {
+
+        @Override
+        public MoxyJsonConfig getContext(final Class<?> objectType) {
+            final MoxyJsonConfig configuration = new MoxyJsonConfig();
+
+            final Map<String, String> namespacePrefixMapper = new HashMap<>(1);
+            namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+            namespacePrefixMapper.put("http://example.com", "example");
+            namespacePrefixMapper.put("http://test.jaxb.com", "jaxb");
+
+            configuration.setNamespacePrefixMapper(namespacePrefixMapper);
+            configuration.setNamespaceSeparator(':');
+
+            return configuration;
+        }
+    }
+
+    @Provider
+    protected static final class JsonbContextResolver implements ContextResolver<Jsonb> {
+
+        @Override
+        public Jsonb getContext(Class<?> type) {
+            JsonbConfig config = new JsonbConfig();
+            return JsonbBuilder.create(config);
+        }
+    }
+
+    public static class JacksonJsonTestProvider extends JsonTestProvider {
+
+        public JacksonJsonTestProvider() {
+            setFeature(new JacksonFeature());
+        }
+
+    }
+
+    public static class Jackson1JsonTestProvider extends JsonTestProvider {
+        public Jackson1JsonTestProvider() {
+            setFeature(new Jackson1Feature());
+        }
+    }
+
+    public static class JsonbTestProvider extends JsonTestProvider {
+        public JsonbTestProvider() {
+            setFeature(new JsonBindingFeature());
+            getProviders().add(new JsonbContextResolver());
+        }
+    }
+
+    public JettisonConfig getConfiguration() {
+        return configuration;
+    }
+
+    protected void setConfiguration(final JettisonConfig configuration) {
+        this.configuration = configuration;
+    }
+
+    public Feature getFeature() {
+        return feature;
+    }
+
+    protected void setFeature(final Feature feature) {
+        this.feature = feature;
+    }
+
+    public Set<Object> getProviders() {
+        return providers;
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingEncodingFilterTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingEncodingFilterTest.java
new file mode 100644
index 0000000..2d2e50c
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingEncodingFilterTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014, 2018 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.json;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.DeflateEncoder;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.server.JSONP;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.filter.EncodingFilter;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests, that JSONP callback wrapping takes places before the eventual entity compression.
+ *
+ * See https://java.net/jira/browse/JERSEY-2524 for the original issue description.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class JsonWithPaddingEncodingFilterTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(MyResource.class)
+                .register(JacksonFeature.class)
+                .register(EncodingFilter.class)
+                .register(GZipEncoder.class)
+                .register(DeflateEncoder.class);
+    }
+
+    @Path("rest")
+    public static class MyResource {
+        @GET
+        @Path("jsonp")
+        @JSONP(queryParam = JSONP.DEFAULT_QUERY)
+        @Produces("application/x-javascript")
+        public Message getHelloJsonP(@Context final HttpHeaders headers) {
+            final MultivaluedMap<String, String> headerParams = headers.getRequestHeaders();
+            for (final String key : headerParams.keySet()) {
+                System.out.println(key + ": ");
+                for (final String value : headerParams.get(key)) {
+                    System.out.print(value + ", ");
+                }
+                System.out.println("\b\b");
+            }
+            return new Message("Hello world JsonP!", "English");
+        }
+    }
+
+    public static class Message {
+        private String greeting;
+        private String language;
+
+        public Message(final String greeting, final String language) {
+            this.greeting = greeting;
+            this.language = language;
+        }
+
+        public String getGreeting() {
+            return greeting;
+        }
+
+        public String getLanguage() {
+            return language;
+        }
+    }
+
+    @Test
+    public void testCorrectGzipDecoding() {
+        final Response response = target().path("rest/jsonp").queryParam("__callback", "dialog")
+                .register(GZipEncoder.class).request("application/x-javascript")
+                .header("Accept-Encoding", "gzip").get();
+
+        final String result = response.readEntity(String.class);
+        assertEquals("gzip", response.getHeaders().getFirst("Content-Encoding"));
+
+        assertTrue(result.startsWith("dialog("));
+        assertTrue(result.contains("Hello world JsonP!"));
+        assertTrue(result.contains("English"));
+    }
+
+    @Test
+    public void testCorrectDeflateDecoding() {
+        final Response response = target().path("rest/jsonp").queryParam("__callback", "dialog")
+                .register(DeflateEncoder.class).request("application/x-javascript")
+                .header("Accept-Encoding", "deflate").get();
+
+        final String result = response.readEntity(String.class);
+        assertEquals("deflate", response.getHeaders().getFirst("Content-Encoding"));
+
+        assertTrue(result.startsWith("dialog("));
+        assertTrue(result.contains("Hello world JsonP!"));
+        assertTrue(result.contains("English"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingTest.java
new file mode 100644
index 0000000..17f35df
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.JSONP;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class JsonWithPaddingTest extends JerseyTest {
+
+    @SuppressWarnings("UnusedDeclaration")
+    @XmlRootElement
+    public static class JsonBean {
+
+        private String attribute;
+
+        public JsonBean() {
+        }
+
+        public JsonBean(final String attr) {
+            this.attribute = attr;
+        }
+
+        public static JsonBean createTestInstance() {
+            return new JsonBean("attr");
+        }
+
+        public String getAttribute() {
+            return attribute;
+        }
+
+        public void setAttribute(final String attribute) {
+            this.attribute = attribute;
+        }
+    }
+
+    @Path("jsonp")
+    @Produces({"application/x-javascript", "application/json"})
+    public static class JsonResource {
+
+        @GET
+        @Path("PureJson")
+        public JsonBean getPureJson() {
+            return JsonBean.createTestInstance();
+        }
+
+        @GET
+        @JSONP
+        @Path("JsonWithPaddingDefault")
+        public JsonBean getJsonWithPaddingDefault() {
+            return JsonBean.createTestInstance();
+        }
+
+        @GET
+        @JSONP(queryParam = "eval")
+        @Path("JsonWithPaddingQueryCallbackParam")
+        public JsonBean getJsonWithPaddingQueryCallbackParam() {
+            return JsonBean.createTestInstance();
+        }
+
+        @GET
+        @JSONP(callback = "parse", queryParam = "eval")
+        @Path("JsonWithPaddingCallbackAndQueryCallbackParam")
+        public JsonBean getJsonWithPaddingCallbackAndQueryCallbackParam() {
+            return JsonBean.createTestInstance();
+        }
+
+        @GET
+        @JSONP(callback = "eval")
+        @Path("JsonWithPaddingCallback")
+        public JsonBean getJsonWithPaddingCallback() {
+            return JsonBean.createTestInstance();
+        }
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<JsonTestProvider[]> getJsonProviders() throws Exception {
+        final List<JsonTestProvider[]> testProviders = new LinkedList<>();
+
+        for (final JsonTestProvider jsonProvider : JsonTestProvider.JAXB_PROVIDERS) {
+            testProviders.add(new JsonTestProvider[] {jsonProvider});
+        }
+
+        return testProviders;
+    }
+
+    private final JsonTestProvider jsonTestProvider;
+    private final String errorMessage;
+
+    public JsonWithPaddingTest(final JsonTestProvider jsonTestProvider) throws Exception {
+        super(configureJaxrsApplication(jsonTestProvider));
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        this.jsonTestProvider = jsonTestProvider;
+        this.errorMessage = String.format("%s: Received JSON entity content does not match expected JSON entity content.",
+                jsonTestProvider.getClass().getSimpleName());
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(jsonTestProvider.getFeature());
+    }
+
+    private static Application configureJaxrsApplication(final JsonTestProvider jsonTestProvider) {
+        final ResourceConfig resourceConfig = new ResourceConfig()
+                .registerClasses(JsonResource.class)
+                .register(jsonTestProvider.getFeature());
+
+        if (jsonTestProvider.getProviders() != null) {
+            resourceConfig.registerInstances(jsonTestProvider.getProviders());
+        }
+
+        return resourceConfig;
+    }
+
+    @Test
+    public void testJson() throws Exception {
+        final Response response = target("jsonp").path("PureJson").request("application/json").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.getMediaType().toString(), equalTo("application/json"));
+
+        final String entity = response.readEntity(String.class);
+
+        assertThat(errorMessage, entity, allOf(not(startsWith("callback(")), not(endsWith(")"))));
+    }
+
+    @Test
+    public void testJsonWithJavaScriptMediaType() throws Exception {
+        final Response response = target("jsonp").path("PureJson").request("application/x-javascript").get();
+
+        // Method is invoked but we do not have a MBW for application/x-javascript.
+        if (jsonTestProvider.getFeature().getClass() == JacksonFeature.class) {
+            assertThat(response.getStatus(), equalTo(200));
+        } else {
+            assertThat(response.getStatus(), equalTo(500));
+        }
+    }
+
+    @Test
+    public void testJsonWithPaddingDefault() throws Exception {
+        test("JsonWithPaddingDefault", "callback");
+    }
+
+    @Test
+    public void testJsonWithPaddingQueryCallbackParam() throws Exception {
+        test("JsonWithPaddingQueryCallbackParam", "eval", "parse");
+    }
+
+    @Test
+    public void testJsonWithPaddingQueryCallbackParamDefaultQueryParam() throws Exception {
+        test("JsonWithPaddingQueryCallbackParam", "callback", "parse", "callback");
+    }
+
+    @Test
+    public void testJsonWithPaddingQueryCallbackParamDefaultCallback() throws Exception {
+        test("JsonWithPaddingQueryCallbackParam", null, "callback");
+    }
+
+    @Test
+    public void testJsonWithPaddingQueryCallbackParamNegative() throws Exception {
+        test("JsonWithPaddingQueryCallbackParam", "call", "parse", true);
+    }
+
+    @Test
+    public void testJsonWithPaddingCallbackAndQueryCallbackParam() throws Exception {
+        test("JsonWithPaddingCallbackAndQueryCallbackParam", "eval", "run");
+    }
+
+    @Test
+    public void testJsonWithPaddingCallbackAndQueryCallbackParamNegative() throws Exception {
+        test("JsonWithPaddingCallbackAndQueryCallbackParam", "eval", "run", "parse", true);
+    }
+
+    @Test
+    public void testJsonWithPaddingCallbackAndQueryCallbackParamDefault() throws Exception {
+        test("JsonWithPaddingCallbackAndQueryCallbackParam", "evalx", "parse");
+    }
+
+    @Test
+    public void testJsonWithPaddingCallbackAndQueryCallbackParamDefaultNegative() throws Exception {
+        test("JsonWithPaddingCallbackAndQueryCallbackParam", "evalx", "xlave", "eval", true);
+    }
+
+    @Test
+    public void testJsonWithPaddingCallback() throws Exception {
+        test("JsonWithPaddingCallback", "eval", "eval");
+    }
+
+    @Test
+    public void testJsonWithPaddingCallbackNegative() throws Exception {
+        test("JsonWithPaddingCallback", "eval", "lave", true);
+    }
+
+    private void test(final String path, final String callback) {
+        test(path, null, null, callback);
+    }
+
+    private void test(final String path, final String queryParamName, final String callback) {
+        test(path, queryParamName, callback, callback, false);
+    }
+
+    private void test(final String path, final String queryParamName, final String callback, final boolean isNegative) {
+        test(path, queryParamName, callback, callback, isNegative);
+    }
+
+    private void test(final String path, final String queryParamName, final String queryParamValue, final String callback) {
+        test(path, queryParamName, queryParamValue, callback, false);
+    }
+
+    private void test(final String path, final String queryParamName, final String queryParamValue, final String callback,
+                      final boolean isNegative) {
+
+        WebTarget tempTarget = target("jsonp").path(path);
+        if (queryParamName != null) {
+            tempTarget = tempTarget.queryParam(queryParamName, queryParamValue);
+        }
+
+        final Response response = tempTarget.request("application/x-javascript").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.getMediaType().toString(), equalTo("application/x-javascript"));
+
+        final String entity = response.readEntity(String.class);
+
+        // Check the entity.
+        final Matcher<String> startsWith = startsWith(callback + "(");
+        final Matcher<String> endsWith = endsWith(")");
+
+        final Matcher<String> callbackMatcher = isNegative ? not(startsWith) : startsWith;
+
+        assertThat(errorMessage, entity, allOf(callbackMatcher, endsWith));
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/PojoTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/PojoTest.java
new file mode 100644
index 0000000..26475b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/PojoTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 2018 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.json;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.glassfish.jersey.tests.e2e.json.entity.pojo.PojoAnimal;
+import org.glassfish.jersey.tests.e2e.json.entity.pojo.PojoAnimalList;
+import org.glassfish.jersey.tests.e2e.json.entity.pojo.PojoCat;
+import org.glassfish.jersey.tests.e2e.json.entity.pojo.PojoDog;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class PojoTest extends AbstractJsonTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<JsonTestSetup[]> generateTestCases() throws Exception {
+        final List<JsonTestSetup[]> jsonTestSetups = new LinkedList<>();
+        final Class<?>[] classes = {PojoAnimalList.class, PojoAnimal.class, PojoDog.class, PojoCat.class};
+
+        for (final JsonTestProvider jsonProvider : JsonTestProvider.POJO_PROVIDERS) {
+            jsonTestSetups.add(new JsonTestSetup[]{
+                    new JsonTestSetup(classes, jsonProvider)
+            });
+        }
+
+        return jsonTestSetups;
+    }
+
+    public PojoTest(final JsonTestSetup jsonTestSetup) throws Exception {
+        super(jsonTestSetup);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Animal.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Animal.java
new file mode 100644
index 0000000..1073589
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Animal.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+// Jackson 1
+@org.codehaus.jackson.annotate.JsonTypeInfo(
+        use = org.codehaus.jackson.annotate.JsonTypeInfo.Id.NAME,
+        include = org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)
+@org.codehaus.jackson.annotate.JsonSubTypes({
+        @org.codehaus.jackson.annotate.JsonSubTypes.Type(value = Cat.class),
+        @org.codehaus.jackson.annotate.JsonSubTypes.Type(value = Dog.class) })
+// Jackson 2
+@com.fasterxml.jackson.annotation.JsonTypeInfo(
+        use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME,
+        include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY)
+@com.fasterxml.jackson.annotation.JsonSubTypes({
+        @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = Cat.class),
+        @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = Dog.class) })
+//
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "animal")
+public class Animal {
+
+    public String name;
+
+    public Animal() {
+    }
+
+    public Animal(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Animal other = (Animal) obj;
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"name\":\"%s\"}", name);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnimalList.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnimalList.java
new file mode 100644
index 0000000..59d7fbe
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnimalList.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "animalList")
+public class AnimalList {
+
+    public List<Animal> animals;
+
+    public static Object createTestInstance() {
+        final AnimalList aList = new AnimalList();
+        aList.animals = new LinkedList<>();
+        aList.animals.add(new Dog("Fifi"));
+        aList.animals.add(new Cat("Daisy"));
+        return aList;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AnimalList other = (AnimalList) obj;
+        if (this.animals != other.animals && (this.animals == null || !this.animals.equals(other.animals))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 89 * hash + (this.animals != null ? this.animals.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (animals != null) ? animals.toString() : null;
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherAnimal.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherAnimal.java
new file mode 100644
index 0000000..a41bbfc
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherAnimal.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "animal")
+public class AnotherAnimal {
+
+    public String name;
+
+    public AnotherAnimal() {
+    }
+
+    public AnotherAnimal(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AnotherAnimal other = (AnotherAnimal) obj;
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"name\":\"%s\"}", name);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherArrayTestBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherArrayTestBean.java
new file mode 100644
index 0000000..6daa660
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherArrayTestBean.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.List;
+
+import javax.json.bind.annotation.JsonbTransient;
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+@SuppressWarnings({"UnusedDeclaration", "SimplifiableIfStatement", "StringEquality"})
+@XmlRootElement()
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class AnotherArrayTestBean {
+
+    public static Object createTestInstance() {
+        AnotherArrayTestBean one = new AnotherArrayTestBean();
+        AnotherCat c1 = new AnotherCat("Foo", "Kitty");
+        one.addCat(c1);
+        AnotherCat c2 = new AnotherCat("Bar", "Puss");
+        one.addCat(c2);
+
+        one.setProp("testProp");
+
+        return one;
+    }
+
+    @XmlElement(required = true)
+    protected List<AnotherCat> cats;
+    protected String prop;
+
+    public AnotherArrayTestBean() {
+        this.cats = new ArrayList<>();
+    }
+
+    public void setCats(List<AnotherCat> cats) {
+        this.cats = cats;
+    }
+
+    @JsonbTransient
+    @XmlTransient
+    public List<AnotherCat> getTheCats() {
+        return this.cats;
+    }
+
+    public void addCat(AnotherCat c) {
+        this.cats.add(c);
+    }
+
+    public String getProp() {
+        return prop;
+    }
+
+    public void setProp(String prop) {
+        this.prop = prop;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AnotherArrayTestBean other = (AnotherArrayTestBean) obj;
+        if (this.prop != other.prop && (this.prop == null || !this.prop.equals(other.prop))) {
+            return false;
+        }
+        return JsonTestHelper.areCollectionsEqual(cats, other.cats);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 79 * hash + (this.prop != null ? this.prop.hashCode() : 0);
+        hash = 79 * hash + (this.cats != null ? this.cats.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("AATB(a=%s, cd=%s)", prop, cats).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherCat.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherCat.java
new file mode 100644
index 0000000..3d260ec
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherCat.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement(name = "cat")
+public class AnotherCat extends AnotherAnimal {
+
+    private String nickName;
+
+    public AnotherCat() {
+    }
+
+    public AnotherCat(String name) {
+        super(name);
+    }
+
+    public AnotherCat(String name, String nickName) {
+        super(name);
+        this.nickName = nickName;
+    }
+
+    public void setNickName(String nickName) {
+        this.nickName = nickName;
+    }
+
+    public String getNickName() {
+        return this.nickName;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"cat\" : %s , %s}", super.toString(), this.nickName);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AttrAndCharDataBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AttrAndCharDataBean.java
new file mode 100644
index 0000000..b39cbf4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AttrAndCharDataBean.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Formatter;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement"})
+@XmlRootElement(name = "parent")
+public class AttrAndCharDataBean {
+
+    @XmlAttribute
+    public String attr;
+    @XmlValue
+    public String value;
+
+    public static Object createTestInstance() {
+        AttrAndCharDataBean instance = new AttrAndCharDataBean();
+        instance.attr = "aval";
+        instance.value = "pval";
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AttrAndCharDataBean other = (AttrAndCharDataBean) obj;
+        if (this.attr != other.attr && (this.attr == null || !this.attr.equals(other.attr))) {
+            return false;
+        }
+        if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 79 * hash + (this.attr != null ? this.attr.hashCode() : 0);
+        hash = 79 * hash + (this.value != null ? this.value.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("ACD(a=%s, cd=%s)", attr, value).toString();
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Cat.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Cat.java
new file mode 100644
index 0000000..b962e8b
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Cat.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement(name = "cat")
+public class Cat extends Animal {
+
+    private String nickName;
+
+    public Cat() {
+    }
+
+    public Cat(final String name) {
+        super(name);
+    }
+
+    public Cat(final String name, final String nickName) {
+        super(name);
+        this.nickName = nickName;
+    }
+
+    public void setNickName(final String nickName) {
+        this.nickName = nickName;
+    }
+
+    public String getNickName() {
+        return this.nickName;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"cat\" : %s , %s}", super.toString(), this.nickName);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Color.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Color.java
new file mode 100644
index 0000000..79272c2
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Color.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+/**
+ * @author Michal Gajdos
+ */
+public enum Color {
+    RED,
+    GREEN,
+    BLUE
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ColorHolder.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ColorHolder.java
new file mode 100644
index 0000000..b9e5cfd
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ColorHolder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import java.util.Formatter;
+import java.util.Set;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement
+public class ColorHolder {
+
+    private Set<Color> colors;
+
+    public ColorHolder() {
+    }
+
+    public ColorHolder(final Set<Color> colors) {
+        this.colors = colors;
+    }
+
+    public Set<Color> getColors() {
+        return colors;
+    }
+
+    public void setColors(final Set<Color> colors) {
+        this.colors = colors;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof ColorHolder)) {
+            return false;
+        }
+
+        final ColorHolder other = (ColorHolder) obj;
+
+        return JsonTestHelper.areCollectionsEqual(this.colors, other.colors);
+    }
+
+    @Override
+    public int hashCode() {
+        return colors == null ? super.hashCode() : 19 * colors.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return new Formatter().format("CH(%s)", colors).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes.java
new file mode 100644
index 0000000..bfbb41f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Formatter;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement"})
+@XmlRootElement
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class ComplexBeanWithAttributes {
+
+    @XmlAttribute
+    public String a1;
+    @XmlAttribute
+    public int a2;
+    @XmlElement
+    public String filler1;
+    @XmlElement
+    public List<SimpleBeanWithAttributes> list;
+    @XmlElement
+    public String filler2;
+    @XmlElement
+    SimpleBeanWithAttributes b;
+
+    public static Object createTestInstance() {
+        ComplexBeanWithAttributes instance = new ComplexBeanWithAttributes();
+        instance.a1 = "hello dolly";
+        instance.a2 = 31415926;
+        instance.filler1 = "111";
+        instance.filler2 = "222";
+        instance.b = JsonTestHelper.createTestInstance(SimpleBeanWithAttributes.class);
+        instance.list = new LinkedList<>();
+        instance.list.add(JsonTestHelper.createTestInstance(SimpleBeanWithAttributes.class));
+        instance.list.add(JsonTestHelper.createTestInstance(SimpleBeanWithAttributes.class));
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ComplexBeanWithAttributes)) {
+            return false;
+        }
+        final ComplexBeanWithAttributes other = (ComplexBeanWithAttributes) obj;
+        if (this.a1 != other.a1 && (this.a1 == null || !this.a1.equals(other.a1))) {
+            return false;
+        }
+        if (this.a2 != other.a2) {
+            return false;
+        }
+        if (this.b != other.b && (this.b == null || !this.b.equals(other.b))) {
+            return false;
+        }
+        if (this.filler1 != other.filler1 && (this.filler1 == null || !this.filler1.equals(other.filler1))) {
+            return false;
+        }
+        if (this.filler2 != other.filler2 && (this.filler2 == null || !this.filler2.equals(other.filler2))) {
+            return false;
+        }
+        if (this.list != other.list && (this.list == null || !this.list.equals(other.list))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 19 * hash + (this.a1 != null ? this.a1.hashCode() : 0);
+        hash = 19 * hash + this.a2;
+        hash = 19 * hash + (this.b != null ? this.b.hashCode() : 0);
+        hash = 19 * hash + (this.filler1 != null ? this.filler1.hashCode() : 0);
+        hash = 19 * hash + (this.filler2 != null ? this.filler2.hashCode() : 0);
+        hash = 19 * hash + (this.list != null ? this.list.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("CBWA(%s,%d,%s)", a1, a2, b).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes2.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes2.java
new file mode 100644
index 0000000..d247ca7
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes2.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("StringEquality")
+@XmlRootElement
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class ComplexBeanWithAttributes2 {
+
+    @XmlAttribute
+    public String a1;
+    @XmlAttribute
+    public int a2;
+    @XmlElement
+    public String filler1;
+    @XmlElement
+    public List<SimpleBeanWithJustOneAttribute> list;
+    @XmlElement
+    public String filler2;
+    @XmlElement
+    SimpleBeanWithJustOneAttribute b;
+
+    public static Object createTestInstance() {
+        ComplexBeanWithAttributes2 instance = new ComplexBeanWithAttributes2();
+        instance.a1 = "hello dolly";
+        instance.a2 = 31415926;
+        instance.filler1 = "111";
+        instance.filler2 = "222";
+        instance.b = JsonTestHelper.createTestInstance(SimpleBeanWithJustOneAttribute.class);
+        instance.list = new LinkedList<SimpleBeanWithJustOneAttribute>();
+        instance.list.add(JsonTestHelper.createTestInstance(SimpleBeanWithJustOneAttribute.class));
+        instance.list.add(JsonTestHelper.createTestInstance(SimpleBeanWithJustOneAttribute.class));
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ComplexBeanWithAttributes2)) {
+            return false;
+        }
+        final ComplexBeanWithAttributes2 other = (ComplexBeanWithAttributes2) obj;
+        if (this.a1 != other.a1 && (this.a1 == null || !this.a1.equals(other.a1))) {
+            System.out.println("a1 differs");
+            return false;
+        }
+        if (this.a2 != other.a2) {
+            System.out.println("a2 differs");
+            return false;
+        }
+        if (this.b != other.b && (this.b == null || !this.b.equals(other.b))) {
+            System.out.println("b differs");
+            return false;
+        }
+        if (this.filler1 != other.filler1 && (this.filler1 == null || !this.filler1.equals(other.filler1))) {
+            System.out.println("f1 differs");
+            return false;
+        }
+        if (this.filler2 != other.filler2 && (this.filler2 == null || !this.filler2.equals(other.filler2))) {
+            System.out.println("f2 differs");
+            return false;
+        }
+        if (this.list != other.list && (this.list == null || !this.list.equals(other.list))) {
+            System.out.println("list differs");
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 19 * hash + (this.a1 != null ? this.a1.hashCode() : 0);
+        hash = 19 * hash + this.a2;
+        hash = 19 * hash + (this.b != null ? this.b.hashCode() : 0);
+        hash = 19 * hash + (this.filler1 != null ? this.filler1.hashCode() : 0);
+        hash = 19 * hash + (this.filler2 != null ? this.filler2.hashCode() : 0);
+        hash = 19 * hash + (this.list != null ? this.list.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("CBWA2(%s,%d,%s,%s)", a1, a2, b, listAsString());
+    }
+
+    private String listAsString() {
+        return (list != null) ? list.toString() : null;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes3.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes3.java
new file mode 100644
index 0000000..a7b6774
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes3.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class ComplexBeanWithAttributes3 {
+
+    @XmlElement
+    SimpleBeanWithJustOneAttribute b, c;
+
+    public static Object createTestInstance() {
+        ComplexBeanWithAttributes3 instance = new ComplexBeanWithAttributes3();
+        instance.b = JsonTestHelper.createTestInstance(SimpleBeanWithJustOneAttribute.class);
+        instance.c = JsonTestHelper.createTestInstance(SimpleBeanWithJustOneAttribute.class);
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ComplexBeanWithAttributes3)) {
+            return false;
+        }
+        final ComplexBeanWithAttributes3 other = (ComplexBeanWithAttributes3) obj;
+        if (this.b != other.b && (this.b == null || !this.b.equals(other.b))) {
+            System.out.println("b differs");
+            return false;
+        }
+        if (this.c != other.c && (this.c == null || !this.c.equals(other.c))) {
+            System.out.println("c differs");
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 19 * hash + (this.b != null ? this.b.hashCode() : 0);
+        hash = 19 * hash + (this.c != null ? this.c.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("CBWA2(%s,%s)", b, c);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes4.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes4.java
new file mode 100644
index 0000000..6e674d5
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes4.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import java.util.Formatter;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement", "NumberEquality"})
+@XmlRootElement
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class ComplexBeanWithAttributes4 {
+
+    @XmlAttribute
+    public String a1;
+    @XmlAttribute
+    public Integer a2;
+    @XmlElement
+    public String filler1;
+    @XmlElement
+    public List<SimpleBeanWithObjectAttributes> list;
+    @XmlElement
+    public String filler2;
+    @XmlElement
+    SimpleBeanWithObjectAttributes b;
+
+    public static Object createTestInstance() {
+        ComplexBeanWithAttributes4 instance = new ComplexBeanWithAttributes4();
+        instance.b = new SimpleBeanWithObjectAttributes();
+        instance.list = new LinkedList<>();
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ComplexBeanWithAttributes4)) {
+            return false;
+        }
+        final ComplexBeanWithAttributes4 other = (ComplexBeanWithAttributes4) obj;
+        if (this.a1 != other.a1 && (this.a1 == null || !this.a1.equals(other.a1))) {
+            return false;
+        }
+        if (this.a2 != other.a2 && (this.a2 == null || !this.a2.equals(other.a2))) {
+            return false;
+        }
+        if (this.b != other.b && (this.b == null || !this.b.equals(other.b))) {
+            return false;
+        }
+        if (this.filler1 != other.filler1 && (this.filler1 == null || !this.filler1.equals(other.filler1))) {
+            return false;
+        }
+        if (this.filler2 != other.filler2 && (this.filler2 == null || !this.filler2.equals(other.filler2))) {
+            return false;
+        }
+        if (!JsonTestHelper.areCollectionsEqual(this.list, other.list)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 19 * hash + (this.a1 != null ? this.a1.hashCode() : 0);
+        hash = 19 * hash + this.a2;
+        hash = 19 * hash + (this.b != null ? this.b.hashCode() : 0);
+        hash = 19 * hash + (this.filler1 != null ? this.filler1.hashCode() : 0);
+        hash = 19 * hash + (this.filler2 != null ? this.filler2.hashCode() : 0);
+        hash = 19 * hash + (this.list != null ? this.list.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("CBWA4(%s, %d, %s)", a1, a2, b).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/CustomJsonbVisibilityStrategy.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/CustomJsonbVisibilityStrategy.java
new file mode 100644
index 0000000..9d6532d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/CustomJsonbVisibilityStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017, 2018 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.json.entity;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import javax.json.bind.config.PropertyVisibilityStrategy;
+
+/**
+ * Custom, permissive {@link PropertyVisibilityStrategy}.
+ * <p>
+ * Used to allow JSON-B provider to "see" protected/private fields for test purposes.
+ */
+public class CustomJsonbVisibilityStrategy implements PropertyVisibilityStrategy {
+
+    @Override
+    public boolean isVisible(Field field) {
+        return true;
+    }
+
+    @Override
+    public boolean isVisible(Method method) {
+        return true;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Dog.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Dog.java
new file mode 100644
index 0000000..4de2631
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Dog.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement(name = "dog")
+public class Dog extends Animal {
+
+    public Dog() {
+    }
+
+    public Dog(final String name) {
+        super(name);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"dog\" : %s }", super.toString());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementBean.java
new file mode 100644
index 0000000..88c21e7
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementBean.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement
+public class EmptyElementBean {
+
+    public String nullOnly = null;
+
+    public static Object createTestInstance() {
+        EmptyElementBean result = new EmptyElementBean();
+        result.nullOnly = null;
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final EmptyElementBean other = (EmptyElementBean) obj;
+        if ((this.nullOnly == null) ? (other.nullOnly != null) : !this.nullOnly.equals(other.nullOnly)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 13 * hash + (this.nullOnly != null ? this.nullOnly.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{nullOnly:%s}", nullOnly);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementContainingBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementContainingBean.java
new file mode 100644
index 0000000..490c280
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementContainingBean.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement
+public class EmptyElementContainingBean {
+
+    public EmptyElementBean emptyBean;
+    public String c;
+    public String d;
+
+    public static Object createTestInstance() {
+        EmptyElementContainingBean result = new EmptyElementContainingBean();
+        result.c = "foo";
+        result.d = "bar";
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final EmptyElementContainingBean other = (EmptyElementContainingBean) obj;
+        if (this.emptyBean != other.emptyBean && (this.emptyBean == null || !this.emptyBean.equals(other.emptyBean))) {
+            return false;
+        }
+        if ((this.c == null) ? (other.c != null) : !this.c.equals(other.c)) {
+            return false;
+        }
+        if ((this.d == null) ? (other.d != null) : !this.d.equals(other.d)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 59 * hash + (this.emptyBean != null ? this.emptyBean.hashCode() : 0);
+        hash = 59 * hash + (this.c != null ? this.c.hashCode() : 0);
+        hash = 59 * hash + (this.d != null ? this.d.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{c:%s,d:%s}", c, d);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EncodedContentBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EncodedContentBean.java
new file mode 100644
index 0000000..b4feee6
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EncodedContentBean.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"RedundantIfStatement", "StringEquality"})
+@XmlRootElement
+public class EncodedContentBean {
+
+    public String one;
+    public String two;
+
+    public static Object createTestInstance() {
+        EncodedContentBean instance = new EncodedContentBean();
+        instance.one = "\tone\n\tbig";
+        instance.two = "haf\u010C";
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final EncodedContentBean other = (EncodedContentBean) obj;
+        if (this.one != other.one && (this.one == null || !this.one.equals(other.one))) {
+            return false;
+        }
+        if (this.two != other.two && (this.two == null || !this.two.equals(other.two))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 17 * hash + (this.one != null ? this.one.hashCode() : 0);
+        hash = 17 * hash + (this.two != null ? this.two.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ECB(%s, %s)", one, two);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/FakeArrayBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/FakeArrayBean.java
new file mode 100644
index 0000000..c42c9d0
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/FakeArrayBean.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Test case for issue#310.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"RedundantIfStatement", "UnusedDeclaration"})
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+        "weight", "color", "name"
+})
+@XmlRootElement()
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class FakeArrayBean {
+
+    protected List<String> weight;
+    @XmlElement(required = true)
+    protected String color;
+    @XmlElement(required = true)
+    protected String name;
+
+    public List<String> getWeight() {
+        if (weight == null) {
+            weight = new ArrayList<>();
+        }
+        return this.weight;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String value) {
+        this.color = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String value) {
+        this.name = value;
+    }
+
+    public static Object createTestInstance() {
+        FakeArrayBean result = new FakeArrayBean();
+
+        result.getWeight().add("1kg");
+        result.getWeight().add("2kg");
+        result.setColor("red");
+        result.setName("bumper");
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final FakeArrayBean other = (FakeArrayBean) obj;
+        if (this.weight != other.weight && (this.weight == null || !this.weight.equals(other.weight))) {
+            return false;
+        }
+        if ((this.color == null) ? (other.color != null) : !this.color.equals(other.color)) {
+            return false;
+        }
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 29 * hash + (this.weight != null ? this.weight.hashCode() : 0);
+        hash = 29 * hash + (this.color != null ? this.color.hashCode() : 0);
+        hash = 29 * hash + (this.name != null ? this.name.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{name:%s, color:%s, weights:%s}", name, color, weight);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/IntArray.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/IntArray.java
new file mode 100644
index 0000000..742865f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/IntArray.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "intArray")
+public class IntArray {
+
+    public int[] intArray;
+
+    public Integer[] integerArray;
+
+    public int number;
+
+    public IntArray() {
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final IntArray other = (IntArray) obj;
+        if (!Arrays.equals(this.intArray, other.intArray)) {
+            return false;
+        }
+        if (!Arrays.deepEquals(this.integerArray, other.integerArray)) {
+            return false;
+        }
+        if (this.number != other.number) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 89 * hash + Arrays.hashCode(this.intArray);
+        hash = 89 * hash + Arrays.deepHashCode(this.integerArray);
+        hash = 89 * hash + this.number;
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"intArray\":%s, \"integerArray\":%s, \"number\":%d}", Arrays.toString(intArray),
+                Arrays.toString(integerArray), number);
+    }
+
+    public static Object createTestInstance() {
+        IntArray result = new IntArray();
+
+        result.number = 8;
+        result.intArray = new int[]{4};
+        result.integerArray = new Integer[]{3};
+
+        return result;
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Jersey1199List.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Jersey1199List.java
new file mode 100644
index 0000000..f293625
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Jersey1199List.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Formatter;
+
+import javax.json.bind.annotation.JsonbTypeAdapter;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.Jersey1199Test;
+
+/**
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"UnusedDeclaration", "NumberEquality", "SimplifiableIfStatement"})
+@XmlRootElement
+public class Jersey1199List {
+
+    public static Object createTestInstance() {
+        final ColorHolder obj1 = new ColorHolder(EnumSet.of(Color.RED, Color.BLUE));
+        final ColorHolder obj2 = new ColorHolder(EnumSet.of(Color.GREEN));
+
+        return new Jersey1199List(new Object[]{obj1, obj2});
+    }
+
+    private Object[] objects;
+    private Integer offset;
+    private Integer total;
+
+    public Jersey1199List() {
+    }
+
+    public Jersey1199List(final Object[] objects) {
+        this.objects = objects;
+        this.offset = 0;
+        this.total = objects.length;
+    }
+
+    // Jackson 1
+    @org.codehaus.jackson.annotate.JsonTypeInfo(
+            use = org.codehaus.jackson.annotate.JsonTypeInfo.Id.NAME,
+            include = org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)
+    @org.codehaus.jackson.annotate.JsonSubTypes({
+            @org.codehaus.jackson.annotate.JsonSubTypes.Type(value = ColorHolder.class)
+    })
+    // Jackson 2
+    @com.fasterxml.jackson.annotation.JsonTypeInfo(
+            use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME,
+            include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY)
+    @com.fasterxml.jackson.annotation.JsonSubTypes({
+            @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = ColorHolder.class)
+    })
+    // JSON-B
+    @JsonbTypeAdapter(Jersey1199Test.JsonbObjectToColorHolderAdapter.class)
+    public Object[] getObjects() {
+        return objects;
+    }
+
+    public void setObjects(final Object[] objects) {
+        this.objects = objects;
+    }
+
+    public Integer getOffset() {
+        return offset;
+    }
+
+    public void setOffset(final Integer offset) {
+        this.offset = offset;
+    }
+
+    public Integer getTotal() {
+        return total;
+    }
+
+    public void setTotal(final Integer total) {
+        this.total = total;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof Jersey1199List)) {
+            return false;
+        }
+
+        final Jersey1199List other = (Jersey1199List) obj;
+
+        if (this.total != other.total && (this.total == null || !this.total.equals(other.total))) {
+            return false;
+        }
+        if (this.offset != other.offset && (this.offset == null || !this.offset.equals(other.offset))) {
+            return false;
+        }
+
+        return Arrays.equals(this.objects, other.objects);
+    }
+
+    @Override
+    public String toString() {
+        return new Formatter().format("Jersey1199List(%s, %d, %d)", Arrays.toString(objects), offset, total).toString();
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 43;
+        hash += (offset != null ? 17 * offset : 0);
+        hash += (total != null ? 17 * total : 0);
+        hash += Arrays.hashCode(objects);
+        return hash;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListAndNonListBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListAndNonListBean.java
new file mode 100644
index 0000000..4b4fbc8
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListAndNonListBean.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"RedundantIfStatement", "StringEquality"})
+@XmlRootElement
+public class ListAndNonListBean {
+
+    public List<String> a;
+    public String d;
+
+    public static Object createTestInstance() {
+        ListAndNonListBean instance = new ListAndNonListBean();
+        instance.a = new LinkedList<String>();
+        instance.a.add("1");
+        instance.d = "2";
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ListAndNonListBean other = (ListAndNonListBean) obj;
+        if (this.a != other.a && (this.a == null || !this.a.equals(other.a))) {
+            return false;
+        }
+        if (this.d != other.d && (this.d == null || !this.d.equals(other.d))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 47 * hash + (this.a != null ? this.a.hashCode() : 0);
+        hash = 47 * hash + (this.d != null ? this.d.hashCode() : 0);
+        return hash;
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListEmptyBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListEmptyBean.java
new file mode 100644
index 0000000..4ff613b
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListEmptyBean.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Formatter;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author mchenryc
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement(name = "listEmptyBean")
+public class ListEmptyBean {
+
+    private List<String> empty;
+
+    public static Object createTestInstance() {
+        ListEmptyBean instance = new ListEmptyBean();
+        instance.empty = new LinkedList<>();
+        return instance;
+    }
+
+    public List<String> getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(List<String> empty) {
+        this.empty = empty;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ListEmptyBean other = (ListEmptyBean) obj;
+        return this.empty == other.empty
+                || (JsonTestHelper.isCollectionEmpty(this.empty) && JsonTestHelper.isCollectionEmpty(other.empty))
+                || (this.empty != null && this.empty.equals(other.empty));
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 31 * hash + (this.empty != null ? this.empty.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("LwNB(n=%d,isNull:%s)", (empty != null) ? empty.size() : 0, (empty == null)).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListWrapperBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListWrapperBean.java
new file mode 100644
index 0000000..03bd242
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListWrapperBean.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "item")
+public class ListWrapperBean {
+
+    public List<String> property;
+
+    public static Object createTestInstance() {
+        return new ListWrapperBean();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ListWrapperBean other = (ListWrapperBean) obj;
+        if ((this.property != other.property
+                && JsonTestHelper.isCollectionEmpty(this.property) != JsonTestHelper.isCollectionEmpty(other.property))
+                && (this.property == null || !this.property.equals(other.property))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 23 * hash + (this.property != null ? this.property.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyError.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyError.java
new file mode 100644
index 0000000..46ab86a
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyError.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * <p>Java class for anonymous complex type.
+ * <p/>
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * <p/>
+ * <pre>
+ * &lt;complexType>
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;sequence>
+ *         &lt;element name="id" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *         &lt;element name="desc" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *       &lt;/sequence>
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+        "id", "desc"
+})
+@XmlRootElement(name = "myError")
+public class MyError {
+
+    @XmlElement(required = true)
+    protected String id;
+    @XmlElement(required = true)
+    protected String desc;
+
+    /**
+     * Gets the value of the id property.
+     *
+     * @return possible object is
+     *         {@link String }
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Sets the value of the id property.
+     *
+     * @param value allowed object is
+     * {@link String }
+     */
+    public void setId(String value) {
+        this.id = value;
+    }
+
+    /**
+     * Gets the value of the desc property.
+     *
+     * @return possible object is
+     *         {@link String }
+     */
+    public String getDesc() {
+        return desc;
+    }
+
+    /**
+     * Sets the value of the desc property.
+     *
+     * @param value allowed object is
+     * {@link String }
+     */
+    public void setDesc(String value) {
+        this.desc = value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final MyError other = (MyError) obj;
+        if ((this.id == null) ? (other.id != null) : !this.id.equals(other.id)) {
+            return false;
+        }
+        if ((this.desc == null) ? (other.desc != null) : !this.desc.equals(other.desc)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + (this.id != null ? this.id.hashCode() : 0);
+        hash = 19 * hash + (this.desc != null ? this.desc.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        sb.append(getId());
+        sb.append(',');
+        sb.append(getDesc());
+        sb.append('}');
+        return sb.toString();
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyMessage.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyMessage.java
new file mode 100644
index 0000000..923b761
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyMessage.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * <p>Java class for anonymous complex type.
+ * <p/>
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * <p/>
+ * <pre>
+ * &lt;complexType>
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;sequence>
+ *         &lt;element name="id" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *         &lt;element name="text" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *       &lt;/sequence>
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+        "id", "text"
+})
+@XmlRootElement(name = "myMessage")
+public class MyMessage {
+
+    @XmlElement(required = true)
+    protected String id;
+    @XmlElement(required = true)
+    protected String text;
+
+    /**
+     * Gets the value of the id property.
+     *
+     * @return possible object is
+     *         {@link String }
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Sets the value of the id property.
+     *
+     * @param value allowed object is
+     * {@link String }
+     */
+    public void setId(String value) {
+        this.id = value;
+    }
+
+    /**
+     * Gets the value of the text property.
+     *
+     * @return possible object is
+     *         {@link String }
+     */
+    public String getText() {
+        return text;
+    }
+
+    /**
+     * Sets the value of the text property.
+     *
+     * @param value allowed object is
+     * {@link String }
+     */
+    public void setText(String value) {
+        this.text = value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final MyMessage other = (MyMessage) obj;
+        if ((this.id == null) ? (other.id != null) : !this.id.equals(other.id)) {
+            return false;
+        }
+        if ((this.text == null) ? (other.text != null) : !this.text.equals(other.text)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 47 * hash + (this.id != null ? this.id.hashCode() : 0);
+        hash = 47 * hash + (this.text != null ? this.text.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        sb.append(getId());
+        sb.append(',');
+        sb.append(getText());
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyResponse.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyResponse.java
new file mode 100644
index 0000000..10f53d2
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyResponse.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.json.bind.annotation.JsonbVisibility;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * <p>Java class for anonymous complex type.
+ * <p/>
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * <p/>
+ * <pre>
+ * &lt;complexType>
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;sequence>
+ *         &lt;element ref="{http://test.jaxb.com}myMessage" maxOccurs="unbounded" minOccurs="0"/>
+ *         &lt;element ref="{http://test.jaxb.com}myError"/>
+ *       &lt;/sequence>
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+        "myMessage", "myError"
+})
+@XmlRootElement(name = "myResponse")
+@JsonbVisibility(CustomJsonbVisibilityStrategy.class)
+public class MyResponse {
+
+    @XmlElement(namespace = "http://test.jaxb.com")
+    protected List<MyMessage> myMessage;
+    @XmlElement(namespace = "http://test.jaxb.com", required = true)
+    protected MyError myError;
+
+    /**
+     * Gets the value of the myMessage property.
+     * <p/>
+     * <p/>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the myMessage property.
+     * <p/>
+     * <p/>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getMyMessage().add(newItem);
+     * </pre>
+     * <p/>
+     * <p/>
+     * <p/>
+     * Objects of the following type(s) are allowed in the list
+     * {@link MyMessage }
+     */
+    public List<MyMessage> getMyMessage() {
+        if (myMessage == null) {
+            myMessage = new ArrayList<>();
+        }
+        return this.myMessage;
+    }
+
+    /**
+     * Gets the value of the myError property.
+     *
+     * @return possible object is
+     *         {@link MyError }
+     */
+    public MyError getMyError() {
+        return myError;
+    }
+
+    /**
+     * Sets the value of the myError property.
+     *
+     * @param value allowed object is
+     * {@link MyError }
+     */
+    public void setMyError(MyError value) {
+        this.myError = value;
+    }
+
+    public static Object createTestInstance() {
+
+        MyResponse myResponse = new MyResponse();
+
+        MyMessage msg = new MyMessage();
+        msg.setId("0");
+        msg.setText("ok");
+        MyMessage msg2 = new MyMessage();
+        msg2.setId("1");
+        msg2.setText("ok");
+        myResponse.getMyMessage().add(msg);
+        myResponse.getMyMessage().add(msg2);
+
+        MyError err = new MyError();
+        err.setId("-1");
+        err.setDesc("error");
+        myResponse.setMyError(err);
+        return myResponse;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final MyResponse other = (MyResponse) obj;
+        if (this.myMessage != other.myMessage && (this.myMessage == null || !this.myMessage.equals(other.myMessage))) {
+            return false;
+        }
+        if (this.myError != other.myError && (this.myError == null || !this.myError.equals(other.myError))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 97 * hash + (this.myMessage != null ? this.myMessage.hashCode() : 0);
+        hash = 97 * hash + (this.myError != null ? this.myError.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        sb.append(getMyMessage());
+        sb.append(",");
+        sb.append(getMyError());
+        sb.append("}");
+        return sb.toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBean.java
new file mode 100644
index 0000000..acbb2c6
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBean.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+
+@SuppressWarnings({"RedundantIfStatement", "UnusedDeclaration"})
+@XmlRootElement
+public class NamespaceBean {
+
+    @XmlElement
+    public String a;
+
+    @XmlElement(namespace = "http://example.com")
+    public String b;
+
+    public NamespaceBean() {
+    }
+
+    public NamespaceBean(String a, String b) {
+        this.a = a;
+        this.b = b;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final NamespaceBean other = (NamespaceBean) obj;
+        if ((this.a == null) ? (other.a != null) : !this.a.equals(other.a)) {
+            return false;
+        }
+        if ((this.b == null) ? (other.b != null) : !this.b.equals(other.b)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 37 * hash + (this.a != null ? this.a.hashCode() : 0);
+        hash = 37 * hash + (this.b != null ? this.b.hashCode() : 0);
+        return hash;
+    }
+
+    public static Object createTestInstance() {
+        return new NamespaceBean("foo", "bar");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{a:%s, b:%s", a, b);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBeanWithAttribute.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBeanWithAttribute.java
new file mode 100644
index 0000000..74254f8
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBeanWithAttribute.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"UnusedDeclaration", "RedundantIfStatement"})
+@XmlRootElement
+public class NamespaceBeanWithAttribute {
+
+    @XmlAttribute(namespace = "http://example.com")
+    public String attr;
+
+    @XmlElement
+    public String a;
+
+    @XmlElement(namespace = "http://example.com")
+    public String b;
+
+    public NamespaceBeanWithAttribute() {
+    }
+
+    public NamespaceBeanWithAttribute(String attr, String a, String b) {
+        this.attr = attr;
+        this.a = a;
+        this.b = b;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final NamespaceBeanWithAttribute other = (NamespaceBeanWithAttribute) obj;
+        if ((this.attr == null) ? (other.attr != null) : !this.attr.equals(other.attr)) {
+            return false;
+        }
+        if ((this.a == null) ? (other.a != null) : !this.a.equals(other.a)) {
+            return false;
+        }
+        if ((this.b == null) ? (other.b != null) : !this.b.equals(other.b)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 37 * hash + (this.attr != null ? this.attr.hashCode() : 0);
+        hash = 37 * hash + (this.a != null ? this.a.hashCode() : 0);
+        hash = 37 * hash + (this.b != null ? this.b.hashCode() : 0);
+        return hash;
+    }
+
+    public static Object createTestInstance() {
+        return new NamespaceBeanWithAttribute("value", "foo", "bar");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{attr:%s, a:%s, b:%s", attr, a, b);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NullStringBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NullStringBean.java
new file mode 100644
index 0000000..ee855d3
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NullStringBean.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement
+public class NullStringBean {
+
+    @XmlElement(nillable = true)
+    public String nullString = "not null to test if set to null works";
+
+    public static Object createTestInstance() {
+        return new NullStringBean();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final NullStringBean other = (NullStringBean) obj;
+        if ((this.nullString == null) ? (other.nullString != null) : !this.nullString.equals(other.nullString)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 19 * hash + (this.nullString != null ? this.nullString.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{nullString:%s}", quoteDelimitedStringOrNull(nullString));
+    }
+
+    private String quoteDelimitedStringOrNull(String string) {
+        return (string == null) ? "null" : String.format("\"%s\"", string);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Person.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Person.java
new file mode 100644
index 0000000..f578874
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Person.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Jay Feenan (jay.feenan at oracle.com)
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement(name = "person")
+@XmlAccessorType(XmlAccessType.PROPERTY)
+@XmlType(propOrder = {"name", "children"})
+public class Person {
+
+    public static Object createTestInstance() {
+        Person daughter = new Person();
+        daughter.setName("Jill Schmo");
+
+        Person son = new Person();
+        son.setName("Jack Schmo");
+
+        Person person = new Person();
+        person.setName("Joe Schmo");
+        person.setChildren(new Person[]{daughter, son});
+
+        return person;
+    }
+
+    @SuppressWarnings("SimplifiableIfStatement")
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if ((this.m_name == null) ? (other.m_name != null) : !this.m_name.equals(other.m_name)) {
+            return false;
+        }
+        return JsonTestHelper.areArraysEqual(m_children, other.m_children);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 19 * hash + (this.m_name != null ? this.m_name.hashCode() : 0);
+        hash = 19 * hash + (this.m_children != null ? Arrays.hashCode(this.m_children) : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{person: %s, %s}", m_name, Arrays.toString(m_children));
+    }
+
+    public String getName() {
+        return m_name;
+    }
+
+    public void setName(String name) {
+        m_name = name;
+    }
+
+    @XmlElementWrapper(name = "children")
+    @XmlElement(name = "child")
+    public Person[] getChildren() {
+        return m_children;
+    }
+
+    public void setChildren(Person[] children) {
+        m_children = children;
+    }
+
+    private String m_name;
+    private Person[] m_children;
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/PureCharDataBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/PureCharDataBean.java
new file mode 100644
index 0000000..bc21834
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/PureCharDataBean.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"RedundantIfStatement", "StringEquality"})
+@XmlRootElement
+public class PureCharDataBean {
+
+    @XmlValue
+    public String content;
+
+    public static Object createTestInstance() {
+        PureCharDataBean instance = new PureCharDataBean();
+        instance.content = "some textual content";
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PureCharDataBean other = (PureCharDataBean) obj;
+        if (this.content != other.content && (this.content == null || !this.content.equals(other.content))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 59 * hash + (this.content != null ? this.content.hashCode() : 0);
+        return hash;
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/RegisterMessage.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/RegisterMessage.java
new file mode 100644
index 0000000..68b5330
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/RegisterMessage.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author David Kaspar
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement
+public final class RegisterMessage {
+
+    @XmlAttribute
+    public String agentUID;
+
+    @XmlAttribute
+    public long requestTime;
+
+    public RegisterMessage() {
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final RegisterMessage other = (RegisterMessage) obj;
+        if ((this.agentUID == null) ? (other.agentUID != null) : !this.agentUID.equals(other.agentUID)) {
+            return false;
+        }
+        if (this.requestTime != other.requestTime) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 89 * hash + (this.agentUID != null ? this.agentUID.hashCode() : 0);
+        hash = 89 * hash + (int) (this.requestTime ^ (this.requestTime >>> 32));
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{\"agentUID\":\"%s\", \"requestTime\":%d}", agentUID, requestTime);
+    }
+
+    public static Object createTestInstance() {
+        RegisterMessage rm = new RegisterMessage();
+        rm.agentUID = "agentKocka";
+        rm.requestTime = 1234L;
+        return rm;
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBean.java
new file mode 100644
index 0000000..79251da
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBean.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Formatter;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"RedundantIfStatement", "StringEquality"})
+@XmlRootElement(name = "DummyOne")
+public class SimpleBean {
+
+    @XmlElement
+    public String child;
+
+    public static Object createTestInstance() {
+        SimpleBean instance = new SimpleBean();
+        instance.child = "simple";
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SimpleBean other = (SimpleBean) obj;
+        if (this.child != other.child && (this.child == null || !this.child.equals(other.child))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 67 * hash + (this.child != null ? this.child.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("SB(%s)", child).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithAttributes.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithAttributes.java
new file mode 100644
index 0000000..e086739
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithAttributes.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Formatter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement"})
+@XmlRootElement
+public class SimpleBeanWithAttributes {
+
+    @XmlAttribute
+    public URI uri;
+    public String s1;
+    @XmlAttribute
+    public int i;
+    @XmlAttribute
+    public String j;
+
+    public SimpleBeanWithAttributes() {
+    }
+
+    public static Object createTestInstance() {
+        SimpleBeanWithAttributes instance = new SimpleBeanWithAttributes();
+        instance.s1 = "hi there";
+        instance.i = 312;
+        instance.j = "bumper";
+
+        try {
+            instance.uri = new URI("http://localhost:8080/jedna/bedna/");
+        } catch (URISyntaxException ex) {
+            Logger.getLogger(SimpleBeanWithAttributes.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SimpleBeanWithAttributes other = (SimpleBeanWithAttributes) obj;
+        if (this.s1 != other.s1 && (this.s1 == null || !this.s1.equals(other.s1))) {
+            return false;
+        }
+        if (this.j != other.j && (this.j == null || !this.j.equals(other.j))) {
+            return false;
+        }
+        if (this.uri != other.uri && (this.uri == null || !this.uri.equals(other.uri))) {
+            return false;
+        }
+        if (this.i != other.i) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        if (null != s1) {
+            hash += 17 * s1.hashCode();
+        }
+        if (null != j) {
+            hash += 17 * j.hashCode();
+        }
+        if (null != uri) {
+            hash += 17 * uri.hashCode();
+        }
+        hash += 13 * i;
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("SBWA(%s,%d,%s,%s)", s1, i, j, uri).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttribute.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttribute.java
new file mode 100644
index 0000000..b3207ab
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttribute.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement
+public class SimpleBeanWithJustOneAttribute {
+
+    @XmlAttribute
+    public URI uri;
+
+    public SimpleBeanWithJustOneAttribute() {
+    }
+
+    public static Object createTestInstance() {
+        SimpleBeanWithJustOneAttribute instance = new SimpleBeanWithJustOneAttribute();
+
+        try {
+            instance.uri = new URI("http://localhost:8080/jedna/bedna/");
+        } catch (URISyntaxException ex) {
+            Logger.getLogger(SimpleBeanWithJustOneAttribute.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SimpleBeanWithJustOneAttribute other = (SimpleBeanWithJustOneAttribute) obj;
+        if (this.uri != other.uri && (this.uri == null || !this.uri.equals(other.uri))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        if (null != uri) {
+            hash += 17 * uri.hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SBWJOA(%s)", uri);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttributeAndValue.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttributeAndValue.java
new file mode 100644
index 0000000..45c794f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttributeAndValue.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"RedundantIfStatement", "StringEquality"})
+@XmlRootElement
+public class SimpleBeanWithJustOneAttributeAndValue {
+
+    @XmlAttribute
+    public URI uri;
+    @XmlValue
+    public String value;
+
+    public SimpleBeanWithJustOneAttributeAndValue() {
+    }
+
+    public static Object createTestInstance() {
+        SimpleBeanWithJustOneAttributeAndValue instance = new SimpleBeanWithJustOneAttributeAndValue();
+
+        try {
+            instance.uri = new URI("http://localhost:8080/jedna/bedna/");
+            instance.value = "characters";
+        } catch (URISyntaxException ex) {
+            Logger.getLogger(SimpleBeanWithJustOneAttributeAndValue.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SimpleBeanWithJustOneAttributeAndValue other = (SimpleBeanWithJustOneAttributeAndValue) obj;
+        if (this.uri != other.uri && (this.uri == null || !this.uri.equals(other.uri))) {
+            return false;
+        }
+        if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        if (null != uri) {
+            hash += 17 * uri.hashCode();
+        }
+        if (null != value) {
+            hash += 17 * value.hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SBWJOAV(%s,%s)", uri, value);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithObjectAttributes.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithObjectAttributes.java
new file mode 100644
index 0000000..bc10df1
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithObjectAttributes.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity;
+
+import java.net.URI;
+import java.util.Formatter;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement", "NumberEquality"})
+@XmlRootElement
+public class SimpleBeanWithObjectAttributes {
+
+    @XmlAttribute
+    public URI uri;
+    public String s1;
+    @XmlAttribute
+    public Integer i;
+    @XmlAttribute
+    public String j;
+
+    public SimpleBeanWithObjectAttributes() {
+    }
+
+    public static Object createTestInstance() {
+        return new SimpleBeanWithObjectAttributes();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SimpleBeanWithObjectAttributes other = (SimpleBeanWithObjectAttributes) obj;
+        if (this.s1 != other.s1 && (this.s1 == null || !this.s1.equals(other.s1))) {
+            return false;
+        }
+        if (this.j != other.j && (this.j == null || !this.j.equals(other.j))) {
+            return false;
+        }
+        if (this.uri != other.uri && (this.uri == null || !this.uri.equals(other.uri))) {
+            return false;
+        }
+        if (this.i != other.i && (this.i == null || !this.i.equals(other.i))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        if (null != s1) {
+            hash += 17 * s1.hashCode();
+        }
+        if (null != j) {
+            hash += 17 * j.hashCode();
+        }
+        if (null != uri) {
+            hash += 17 * uri.hashCode();
+        }
+        hash += 13 * i;
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("SBWOA(%s,%d,%s,%s)", s1, i, j, uri).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleXmlTypeBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleXmlTypeBean.java
new file mode 100644
index 0000000..82cf9ac
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleXmlTypeBean.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Formatter;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement"})
+@XmlType
+public class SimpleXmlTypeBean {
+
+    @XmlElement
+    public String child;
+
+    public static Object createTestInstance() {
+        SimpleXmlTypeBean instance = new SimpleXmlTypeBean();
+        instance.child = "simple";
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SimpleXmlTypeBean other = (SimpleXmlTypeBean) obj;
+        if (this.child != other.child && (this.child == null || !this.child.equals(other.child))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 67 * hash + (this.child != null ? this.child.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (new Formatter()).format("SETB(%s)", child).toString();
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SingleItemListWrapperBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SingleItemListWrapperBean.java
new file mode 100644
index 0000000..f7f14a0
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SingleItemListWrapperBean.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 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.json.entity;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "singleItemListWrapper")
+public class SingleItemListWrapperBean {
+
+    public List<String> singleItemList;
+
+    public static Object createTestInstance() {
+        SingleItemListWrapperBean instance = new SingleItemListWrapperBean();
+        instance.singleItemList = new LinkedList<>();
+        instance.singleItemList.add("1");
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SingleItemListWrapperBean other = (SingleItemListWrapperBean) obj;
+        if (this.singleItemList != other.singleItemList && (this.singleItemList == null || !this.singleItemList
+                .equals(other.singleItemList))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 59 * hash + (this.singleItemList != null ? this.singleItemList.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{singleItemListWrapperBean:{l:%s}}", singleItemList);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TreeModel.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TreeModel.java
new file mode 100644
index 0000000..bc9dc0f
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TreeModel.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"UnusedDeclaration", "RedundantIfStatement"})
+@XmlRootElement
+public class TreeModel {
+
+    @SuppressWarnings({"UnusedDeclaration", "StringEquality", "RedundantIfStatement"})
+    public static class Node {
+
+        @XmlElement
+        public String label;
+        @XmlElement
+        public boolean expanded;
+        @XmlElement
+        public List<Node> children;
+
+        public Node() {
+            this("dummy node", null);
+        }
+
+        public Node(String label) {
+            this(label, null);
+        }
+
+        public Node(String label, Collection<Node> children) {
+            this.label = label;
+            if (!JsonTestHelper.isCollectionEmpty(children)) {
+                this.children = new LinkedList<Node>();
+                this.children.addAll(children);
+                expanded = true;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 13;
+            result += 17 * label.hashCode();
+            if (!JsonTestHelper.isCollectionEmpty(children)) {
+                for (Node n : children) {
+                    result = 5 + 17 * n.hashCode();
+                }
+            }
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Node)) {
+                return false;
+            }
+            final Node other = (Node) obj;
+            if (this.label != other.label && (this.label == null || !this.label.equals(other.label))) {
+                return false;
+            }
+            if ((this.children != other.children
+                    && JsonTestHelper.isCollectionEmpty(this.children) != JsonTestHelper.isCollectionEmpty(other.children))
+                    && (this.children == null || !this.children.equals(other.children))) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            String result = "(" + label + ":";
+            if (!JsonTestHelper.isCollectionEmpty(children)) {
+                for (Node n : children) {
+                    result += n.toString();
+                }
+                return result + ")";
+            } else {
+                return result + "0 children)";
+            }
+        }
+    }
+
+    @XmlElement
+    public Node root;
+
+    public TreeModel() {
+    }
+
+    public TreeModel(Node root) {
+        this.root = root;
+    }
+
+    public static Object createTestInstance() {
+        TreeModel instance = new TreeModel();
+        instance.root = new Node();
+        return instance;
+    }
+
+    @Override
+    public int hashCode() {
+        if (null != root) {
+            return 7 + root.hashCode();
+        } else {
+            return 7;
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof TreeModel)) {
+            return false;
+        }
+        final TreeModel other = (TreeModel) obj;
+        if (this.root != other.root && (this.root == null || !this.root.equals(other.root))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return (null != root) ? root.toString() : "(NULL_ROOT)";
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TwoListsWrapperBean.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TwoListsWrapperBean.java
new file mode 100644
index 0000000..99b76f0
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TwoListsWrapperBean.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings("RedundantIfStatement")
+@XmlRootElement(name = "item2")
+public class TwoListsWrapperBean {
+
+    public List<String> property1, property2;
+
+    public static Object createTestInstance() {
+        TwoListsWrapperBean instance = new TwoListsWrapperBean();
+        instance.property1 = new LinkedList<String>();
+        instance.property1.add("a1");
+        instance.property1.add("a1");
+        instance.property2 = new LinkedList<String>();
+        instance.property2.add("b1");
+        return instance;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final TwoListsWrapperBean other = (TwoListsWrapperBean) obj;
+        if (this.property1 != other.property1 && (this.property1 == null || !this.property1.equals(other.property1))) {
+            return false;
+        }
+        if (this.property2 != other.property2 && (this.property2 == null || !this.property2.equals(other.property2))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 59 * hash + (this.property1 != null ? this.property1.hashCode() : 0);
+        hash = 59 * hash + (this.property2 != null ? this.property2.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{twoListsWrapperBean:{property1:%s, property2:%s}}", property1, property2);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/User.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/User.java
new file mode 100644
index 0000000..94f0649
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/User.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import javax.json.bind.annotation.JsonbTransient;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@SuppressWarnings({"StringEquality", "RedundantIfStatement", "UnusedDeclaration"})
+@XmlRootElement
+public class User {
+
+    @XmlElement(name = "userid")
+    public String id;
+    public String name;
+    @XmlTransient
+    @JsonbTransient
+    public String password;
+
+    public User() {
+    }
+
+    public User(String id, String name, String password) {
+        this.id = id;
+        this.name = name;
+        this.password = password;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof User)) {
+            return false;
+        }
+        final User other = (User) obj;
+        if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
+            return false;
+        }
+        if (this.name != other.name && (this.name == null || !this.name.equals(other.name))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        if (null != id) {
+            hash = 17 * hash + id.hashCode();
+        }
+        if (null != name) {
+            hash = 17 * hash + name.hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return "User(" + id + ", " + name + ")";
+    }
+
+    public static Object createTestInstance() {
+        return new User("1621", "Grotefend", "Persepolis");
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/UserTable.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/UserTable.java
new file mode 100644
index 0000000..a37d11c
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/UserTable.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2010, 2018 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.json.entity;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.json.JsonTestHelper;
+
+/**
+ * Note: With MOXy we need to ensure that collections (a list in this case) with predefined values (assigned to the list during
+ * object initialization) are either uninitialized or empty during the object creation, otherwise there is a possibility that
+ * these default values are doubled in the list (list is filled with default values when a new instance is created and after
+ * unmarshalling XML/JSON stream additional elements are added to this list - MOXy doesn't override the existing list with a
+ * new one created during unmarshalling).
+ * <p/>
+ * Workaround: Set {@link javax.xml.bind.annotation.XmlAccessorType} to {@link javax.xml.bind.annotation.XmlAccessType#FIELD},
+ * do not initialize the list in the default constructor
+ * (field initializer) and assign the value to the list that should contain predefined values manually (in this case the value
+ * object is represented by {@code #DEFAULT_HEADERS}).
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("UnusedDeclaration")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class UserTable {
+
+    @SuppressWarnings("RedundantIfStatement")
+    public static class JMakiTableHeader {
+
+        public String id;
+        public String label;
+
+        public JMakiTableHeader() {
+        }
+
+        public JMakiTableHeader(String id, String label) {
+            this.id = id;
+            this.label = label;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 13;
+            hash = id != null ? 29 * id.hashCode() : hash;
+            hash = label != null ? 29 * label.hashCode() : hash;
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof JMakiTableHeader)) {
+                return false;
+            }
+            JMakiTableHeader that = (JMakiTableHeader) obj;
+
+            if ((id != null && !id.equals(that.id)) && that.id != null) {
+                return false;
+            }
+            if ((label != null && !label.equals(that.label)) && that.label != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "JMakiTableHeader(id = " + id + ", label = " + label + ")";
+        }
+    }
+
+    public static Object createTestInstance() {
+        UserTable instance = new UserTable();
+        instance.rows = new LinkedList<User>();
+        instance.rows.add(JsonTestHelper.createTestInstance(User.class));
+        instance.columns = DEFAULT_HEADERS;
+        return instance;
+    }
+
+    public static Object createTestInstance2() {
+        UserTable instance = new UserTable();
+        instance.rows = new LinkedList<User>();
+        instance.rows.add(JsonTestHelper.createTestInstance(User.class));
+        instance.addColumn(new JMakiTableHeader("password", "Password"));
+        return instance;
+    }
+
+    static List<JMakiTableHeader> initHeaders() {
+        List<JMakiTableHeader> headers = new LinkedList<JMakiTableHeader>();
+        headers.add(new JMakiTableHeader("userid", "UserID"));
+        headers.add(new JMakiTableHeader("name", "User Name"));
+        return Collections.unmodifiableList(headers);
+    }
+
+    public static final List<JMakiTableHeader> DEFAULT_HEADERS = initHeaders();
+
+    private List<JMakiTableHeader> columns;
+    private List<User> rows;
+
+    public UserTable() {
+    }
+
+    public UserTable(List<User> users) {
+        this.rows = new LinkedList<User>();
+        this.rows.addAll(users);
+        this.columns = DEFAULT_HEADERS;
+    }
+
+    public void addColumn(final JMakiTableHeader column) {
+        getColumns().add(column);
+    }
+
+    public List<JMakiTableHeader> getColumns() {
+        if (columns == null) {
+            columns = new LinkedList<JMakiTableHeader>(DEFAULT_HEADERS);
+        }
+        return columns;
+    }
+
+    public void setColumns(final List<JMakiTableHeader> columns) {
+        this.columns = columns;
+    }
+
+    public List<User> getRows() {
+        return rows;
+    }
+
+    public void setRows(final List<User> rows) {
+        this.rows = rows;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof UserTable)) {
+            return false;
+        }
+        final UserTable other = (UserTable) obj;
+
+        return JsonTestHelper.areCollectionsEqual(this.rows, other.rows)
+                && JsonTestHelper.areCollectionsEqual(this.columns, other.columns);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 16;
+        if (null != rows) {
+            for (User u : rows) {
+                hash = 17 * hash + u.hashCode();
+            }
+        }
+        if (null != columns) {
+            for (JMakiTableHeader u : columns) {
+                hash = 17 * hash + u.hashCode();
+            }
+        }
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("UserTable(%s,%s)", rows, columns);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimal.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimal.java
new file mode 100644
index 0000000..e8bd11a
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimal.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity.pojo;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+// Jackson 1
+@org.codehaus.jackson.annotate.JsonTypeInfo(
+        use = org.codehaus.jackson.annotate.JsonTypeInfo.Id.NAME,
+        include = org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)
+@org.codehaus.jackson.annotate.JsonSubTypes({
+        @org.codehaus.jackson.annotate.JsonSubTypes.Type(value = PojoCat.class),
+        @org.codehaus.jackson.annotate.JsonSubTypes.Type(value = PojoDog.class) })
+// Jackson 2
+@com.fasterxml.jackson.annotation.JsonTypeInfo(
+        use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME,
+        include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY)
+@com.fasterxml.jackson.annotation.JsonSubTypes({
+        @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = PojoCat.class),
+        @com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = PojoDog.class) })
+@SuppressWarnings("RedundantIfStatement")
+public class PojoAnimal {
+
+    public String name;
+
+    public PojoAnimal() {
+    }
+
+    public PojoAnimal(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PojoAnimal other = (PojoAnimal) obj;
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"name\":\"%s\"}", name);
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimalList.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimalList.java
new file mode 100644
index 0000000..1f082f4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimalList.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity.pojo;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("RedundantIfStatement")
+public class PojoAnimalList {
+
+    public List<PojoAnimal> animals;
+
+    public static Object createTestInstance() {
+        final PojoAnimalList aList = new PojoAnimalList();
+        aList.animals = new LinkedList<>();
+        aList.animals.add(new PojoDog("Fifi"));
+        aList.animals.add(new PojoCat("Daisy"));
+        return aList;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PojoAnimalList other = (PojoAnimalList) obj;
+        if (this.animals != other.animals && (this.animals == null || !this.animals.equals(other.animals))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 89 * hash + (this.animals != null ? this.animals.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return (animals != null) ? animals.toString() : null;
+    }
+
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoCat.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoCat.java
new file mode 100644
index 0000000..91c1ec4
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoCat.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity.pojo;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class PojoCat extends PojoAnimal {
+
+    private String nickName;
+
+    public PojoCat() {
+    }
+
+    public PojoCat(final String name) {
+        super(name);
+    }
+
+    public PojoCat(final String name, final String nickName) {
+        super(name);
+        this.nickName = nickName;
+    }
+
+    public void setNickName(final String nickName) {
+        this.nickName = nickName;
+    }
+
+    public String getNickName() {
+        return this.nickName;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"cat\" : %s , %s}", super.toString(), this.nickName);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoDog.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoDog.java
new file mode 100644
index 0000000..a4c4d59
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoDog.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.json.entity.pojo;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class PojoDog extends PojoAnimal {
+
+    public PojoDog() {
+    }
+
+    public PojoDog(final String name) {
+        super(name);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{ \"dog\" : %s }", super.toString());
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterCloseTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterCloseTest.java
new file mode 100644
index 0000000..47cf3d5
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterCloseTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2017, 2018 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.sse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.sse.OutboundSseEvent;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseBroadcaster;
+import javax.ws.rs.sse.SseEventSink;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test broadcaster behaviour when closing.
+ *
+ * Proves, that broadcaster attempts to send the messages remaining in the buffer after it receives the close signal.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class BroadcasterCloseTest extends JerseyTest {
+
+    private static final int SLOW_SUBSCRIBER_LATENCY = 200;
+    private static final int MSG_COUNT = 8;
+    private static final CountDownLatch onCompleteLatch = new CountDownLatch(1);
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SseResource.class);
+    }
+
+    @Path("events")
+    @Singleton
+    public static class SseResource {
+        private final Sse sse;
+        private final SseBroadcaster broadcaster;
+        private final List<String> data = new ArrayList<>();
+
+        public SseResource(@Context final Sse sse) {
+            this.sse = sse;
+            this.broadcaster = sse.newBroadcaster();
+            this.broadcaster.register(new SseEventSink() {
+
+                volatile boolean closed = false;
+
+                @Override
+                public boolean isClosed() {
+                    return closed;
+                }
+
+                @Override
+                public CompletionStage<?> send(OutboundSseEvent event) {
+                    try {
+                        Thread.sleep(SLOW_SUBSCRIBER_LATENCY);
+                    } catch (InterruptedException e) {
+                        System.out.println("Slow subscriber's sleep was interrupted.");
+                    }
+                    data.add("" + event.getData());
+
+                    return CompletableFuture.completedFuture(null);
+                }
+
+                @Override
+                public void close() {
+                    System.out.println("Slow subscriber completed");
+                    onCompleteLatch.countDown();
+                    this.closed = true;
+                }
+            });
+        }
+
+        @GET
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        public void getServerSentEvents(@Context final SseEventSink eventSink) {
+            broadcaster.register(eventSink);
+        }
+
+        @GET
+        @Path("push/{msg}")
+        public String addMessage(@PathParam("msg") String message) throws InterruptedException {
+            broadcaster.broadcast(sse.newEvent(message));
+            return "Message added.";
+        }
+
+        @GET
+        @Path("close")
+        public String closeMe() {
+            broadcaster.close();
+            return "Closed";
+        }
+
+        @GET
+        @Path("result")
+        public String getResult() {
+            return data.stream().collect(Collectors.joining(","));
+        }
+    }
+
+    @Test
+    public void testBroadcasterKeepsSendingAfterCLose() throws InterruptedException {
+        // push some events to the broadcaster
+        IntStream.range(0, MSG_COUNT).forEach((i) -> {
+            final Response response = target()
+                    .path("events/push/{msg}")
+                    .resolveTemplate("msg", "msg" + i)
+                    .request()
+                    .get();
+            Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        });
+
+        // instruct broadcaster to close
+        final Response response = target().path("events/close").request().get();
+        Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+        // send one more message (should be rejected -> request will fail)
+        final Response badResponse = target()
+                                        .path("events/push/{msg}")
+                                        .resolveTemplate("msg", "too-late")
+                                        .request()
+                                        .get();
+        Assert.assertNotEquals(Response.Status.OK.getStatusCode(), badResponse.getStatus());
+
+        // wait up to latency * msgcount (+1 as reserve) before the server shuts down
+        Assert.assertTrue(onCompleteLatch.await(SLOW_SUBSCRIBER_LATENCY * (MSG_COUNT + 1), TimeUnit.MILLISECONDS));
+
+        // get data gathered by the slow subsciber
+        String result = target().path("events/result").request().get(String.class);
+        final String[] resultArray = result.split(",");
+
+        // check, that broadcaster sent all the buffered events to the subscriber before completely closing
+        Assert.assertEquals(MSG_COUNT, resultArray.length);
+        for (int i = 0; i < MSG_COUNT; i++) {
+            Assert.assertEquals("msg" + i, resultArray[i]);
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
new file mode 100644
index 0000000..f272bf9
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2017, 2018 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.sse;
+
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.OutboundSseEvent;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseBroadcaster;
+import javax.ws.rs.sse.SseEventSink;
+import javax.ws.rs.sse.SseEventSource;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.client.ClientAsyncExecutor;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ManagedAsyncExecutor;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.spi.ThreadPoolExecutorProvider;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Managed executor service injection and propagation into broadcaster test.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class BroadcasterExecutorTest extends JerseyTest {
+
+    private static final String THREAD_PREFIX = "custom-executor-thread";
+    private static Logger LOGGER = Logger.getLogger(BroadcasterExecutorTest.class.getName());
+
+    private static CountDownLatch closeLatch = new CountDownLatch(1);
+    private static CountDownLatch txLatch = new CountDownLatch(2);
+
+    private static boolean sendThreadOk = false;
+    private static boolean onCompleteThreadOk = false;
+
+    @Path("sse")
+    @Singleton
+    public static class SseResource {
+        private final Sse sse;
+        private SseBroadcaster broadcaster;
+
+        public SseResource(@Context final Sse sse) {
+            this.sse = sse;
+            broadcaster = sse.newBroadcaster();
+            System.out.println("Broadcaster created: " + broadcaster);
+        }
+
+        @GET
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        @Path("events")
+        public void getServerSentEvents(@Context final SseEventSink eventSink, @Context final Sse sse) {
+
+            // TODO JAX-RS 2.1
+            broadcaster.register(new SseEventSink() {
+                @Override
+                public boolean isClosed() {
+                    return eventSink.isClosed();
+                }
+
+                @Override
+                public CompletionStage<?> send(OutboundSseEvent event) {
+                    final String name = Thread.currentThread().getName();
+                    LOGGER.info("onNext called with [" + event + "] from " + name);
+                    sendThreadOk = name.startsWith(THREAD_PREFIX);
+                    txLatch.countDown();
+                    return eventSink.send(event);
+                }
+
+                @Override
+                public void close() {
+                    final String name = Thread.currentThread().getName();
+                    LOGGER.info("onComplete called from " + name);
+                    onCompleteThreadOk = name.startsWith(THREAD_PREFIX);
+                    closeLatch.countDown();
+                    eventSink.close();
+                }
+            });
+        }
+
+        @Path("push/{msg}")
+        @GET
+        public String pushMessage(@PathParam("msg") final String msg) {
+            broadcaster.broadcast(sse.newEventBuilder().data(msg).build());
+            return "Broadcasting message: " + msg;
+        }
+
+        @Path("close")
+        @GET
+        public String close() {
+            broadcaster.close();
+            return "Closed.";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig rc = new ResourceConfig(SseResource.class);
+        rc.property(ServerProperties.WADL_FEATURE_DISABLE, true);
+        rc.register(new CustomManagedAsyncExecutorProvider());
+        return rc;
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new CustomClientAsyncExecutor());
+    }
+
+    @ManagedAsyncExecutor
+    private static class CustomManagedAsyncExecutorProvider extends ThreadPoolExecutorProvider {
+        CustomManagedAsyncExecutorProvider() {
+            super("custom-executor-thread");
+        }
+    }
+
+    @ClientAsyncExecutor
+    private static class CustomClientAsyncExecutor extends ThreadPoolExecutorProvider {
+        CustomClientAsyncExecutor() {
+            super("custom-client-executor");
+        }
+    }
+
+    @Test
+    public void test() throws InterruptedException {
+        final String[] onEventThreadName = {""};
+        SseEventSource eventSource = SseEventSource
+                .target(target().path("sse/events"))
+                .build();
+
+        eventSource.register((event) -> {
+                    LOGGER.info("Event: " + event + " from: " + Thread.currentThread().getName());
+                    onEventThreadName[0] = Thread.currentThread().getName();
+                }
+        );
+
+        eventSource.open();
+
+        target().path("sse/push/firstBroadcast").request().get(String.class);
+        target().path("sse/push/secondBroadcast").request().get(String.class);
+        Assert.assertTrue("txLatch time-outed.", txLatch.await(2000, TimeUnit.MILLISECONDS));
+
+        target().path("sse/close").request().get();
+        Assert.assertTrue("closeLatch time-outed.", closeLatch.await(2000, TimeUnit.MILLISECONDS));
+
+        Assert.assertTrue("send either not invoked at all or from wrong thread", sendThreadOk);
+        Assert.assertTrue("onComplete either not invoked at all or from wrong thread", onCompleteThreadOk);
+
+        Assert.assertTrue("Client event called from wrong thread ( " + onEventThreadName[0] + ")",
+               onEventThreadName[0].startsWith("custom-client-executor"));
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterTest.java
new file mode 100644
index 0000000..d7e285d
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2017, 2018 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.sse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseBroadcaster;
+import javax.ws.rs.sse.SseEventSink;
+import javax.ws.rs.sse.SseEventSource;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * JAX-RS {@link javax.ws.rs.sse.SseBroadcaster} test.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class BroadcasterTest extends JerseyTest {
+
+    static final CountDownLatch closeLatch = new CountDownLatch(4);
+    static final CountDownLatch txLatch = new CountDownLatch(4);
+    private static boolean isSingleton = false;
+
+    @Path("sse")
+    @Singleton
+    public static class SseResource {
+        private final Sse sse;
+        private SseBroadcaster broadcaster;
+
+        public SseResource(@Context final Sse sse) {
+            this.sse = sse;
+            broadcaster = sse.newBroadcaster();
+        }
+
+        @GET
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        @Path("events")
+        public void getServerSentEvents(@Context final SseEventSink eventSink, @Context final Sse sse) {
+            isSingleton = this.sse == sse;
+            eventSink.send(sse.newEventBuilder().data("Event1").build());
+            eventSink.send(sse.newEventBuilder().data("Event2").build());
+            eventSink.send(sse.newEventBuilder().data("Event3").build());
+            broadcaster.register(eventSink);
+            broadcaster.onClose((subscriber) -> {
+                if (subscriber == eventSink) {
+                    closeLatch.countDown();
+                }
+            });
+            txLatch.countDown();
+        }
+
+        @Path("push/{msg}")
+        @GET
+        public String pushMessage(@PathParam("msg") final String msg) {
+            broadcaster.broadcast(sse.newEventBuilder().data(msg).build());
+            txLatch.countDown();
+            return "Broadcasting message: " + msg;
+        }
+
+        @Path("close")
+        @GET
+        public String close() {
+            broadcaster.close();
+            return "Closed.";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig rc = new ResourceConfig(SseResource.class);
+        rc.property(ServerProperties.WADL_FEATURE_DISABLE, true);
+        return rc;
+    }
+
+    @Test
+    public void test() throws InterruptedException {
+        SseEventSource eventSourceA = SseEventSource.target(target().path("sse/events")).build();
+        List<String> resultsA1 = new ArrayList<>();
+        List<String> resultsA2 = new ArrayList<>();
+        CountDownLatch a1Latch = new CountDownLatch(5);
+        CountDownLatch a2Latch = new CountDownLatch(5);
+        eventSourceA.register((event) -> {
+            resultsA1.add(event.readData());
+            a1Latch.countDown();
+        });
+        eventSourceA.register((event) -> {
+            resultsA2.add(event.readData());
+            a2Latch.countDown();
+        });
+        eventSourceA.open();
+
+        target().path("sse/push/firstBroadcast").request().get(String.class);
+
+
+        SseEventSource eventSourceB = SseEventSource.target(target().path("sse/events")).build();
+        List<String> resultsB1 = new ArrayList<>();
+        List<String> resultsB2 = new ArrayList<>();
+        CountDownLatch b1Latch = new CountDownLatch(4);
+        CountDownLatch b2Latch = new CountDownLatch(4);
+        eventSourceB.register((event) -> {
+            resultsB1.add(event.readData());
+            b1Latch.countDown();
+        });
+        eventSourceB.register((event) -> {
+            resultsB2.add(event.readData());
+            b2Latch.countDown();
+        });
+        eventSourceB.open();
+
+        target().path("sse/push/secondBroadcast").request().get(String.class);
+
+        Assert.assertTrue("Waiting for resultsA1 to be complete failed.",
+                a1Latch.await(3000, TimeUnit.MILLISECONDS));
+        Assert.assertTrue("Waiting for resultsA2 to be complete failed.",
+                a2Latch.await(3000, TimeUnit.MILLISECONDS));
+
+        Assert.assertTrue("Waiting for resultsB1 to be complete failed.",
+                b1Latch.await(3000, TimeUnit.MILLISECONDS));
+        Assert.assertTrue("Waiting for resultsB2 to be complete failed.",
+                b2Latch.await(3000, TimeUnit.MILLISECONDS));
+
+        Assert.assertTrue(txLatch.await(5000, TimeUnit.MILLISECONDS));
+
+        // Event1, Event2, Event3, firstBroadcast, secondBroadcast
+        Assert.assertEquals("resultsA1 does not contain 5 elements.", 5, resultsA1.size());
+        Assert.assertEquals("resultsA2 does not contain 5 elements.", 5, resultsA2.size());
+        Assert.assertTrue("resultsA1 does not contain expected data",
+                resultsA1.get(0).equals("Event1")
+                        && resultsA1.get(1).equals("Event2")
+                        && resultsA1.get(2).equals("Event3")
+                        && resultsA1.get(3).equals("firstBroadcast")
+                        && resultsA1.get(4).equals("secondBroadcast"));
+
+        Assert.assertTrue("resultsA2 does not contain expected data",
+                resultsA2.get(0).equals("Event1")
+                        && resultsA2.get(1).equals("Event2")
+                        && resultsA2.get(2).equals("Event3")
+                        && resultsA2.get(3).equals("firstBroadcast")
+                        && resultsA2.get(4).equals("secondBroadcast"));
+
+        Assert.assertEquals("resultsB1 does not contain 4 elements.", 4, resultsB1.size());
+        Assert.assertEquals("resultsB2 does not contain 4 elements.", 4, resultsB2.size());
+        Assert.assertTrue("resultsB1 does not contain expected data",
+                resultsB1.get(0).equals("Event1")
+                        && resultsB1.get(1).equals("Event2")
+                        && resultsB1.get(2).equals("Event3")
+                        && resultsB1.get(3).equals("secondBroadcast"));
+
+        Assert.assertTrue("resultsB2 does not contain expected data",
+                resultsB2.get(0).equals("Event1")
+                        && resultsB2.get(1).equals("Event2")
+                        && resultsB2.get(2).equals("Event3")
+                        && resultsB2.get(3).equals("secondBroadcast"));
+        target().path("sse/close").request().get();
+        Assert.assertTrue(closeLatch.await(3000, TimeUnit.MILLISECONDS));
+        Assert.assertTrue("Sse instances injected into resource and constructor differ. Sse should have been injected"
+                + "as a singleton", isSingleton);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventOutputTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventOutputTest.java
new file mode 100644
index 0000000..0f09d26
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventOutputTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2012, 2018 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.sse;
+
+import java.io.IOException;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.media.sse.EventInput;
+import org.glassfish.jersey.media.sse.EventListener;
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.media.sse.InboundEvent;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Event output tests.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class EventOutputTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SseTestResource.class, SseFeature.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(SseFeature.class);
+    }
+
+    /**
+     * SSE Test resource.
+     */
+    @Path("test")
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    public static class SseTestResource {
+
+        @GET
+        @Path("single")
+        public EventOutput getSingleEvent() {
+            final EventOutput output = new EventOutput();
+            try {
+                return output;
+            } finally {
+                new Thread() {
+                    public void run() {
+                        try {
+                            output.write(new OutboundEvent.Builder().data(String.class, "single").build());
+                            output.close();
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            fail();
+                        }
+                    }
+                }.start();
+            }
+        }
+
+        @GET
+        @Path("closed-single")
+        public EventOutput getClosedSingleEvent() throws IOException {
+            final EventOutput output = new EventOutput();
+            output.write(new OutboundEvent.Builder().data(String.class, "closed").build());
+            output.close();
+            return output;
+        }
+
+        @GET
+        @Path("closed-empty")
+        public EventOutput getClosedEmpty() throws IOException {
+            final EventOutput output = new EventOutput();
+            output.close();
+            return output;
+        }
+
+        @GET
+        @Path("charset")
+        @Produces("text/event-stream;charset=utf-8")
+        public EventOutput getSseWithCharset() throws IOException {
+            final EventOutput output = new EventOutput();
+            output.write(new OutboundEvent.Builder().data(String.class, "charset").build());
+            output.close();
+            return output;
+        }
+
+        @GET
+        @Path("comments-only")
+        public EventOutput getCommentsOnlyStream() throws IOException {
+            final EventOutput output = new EventOutput();
+            output.write(new OutboundEvent.Builder().comment("No comment #1").build());
+            output.write(new OutboundEvent.Builder().comment("No comment #2").build());
+            output.close();
+            return output;
+        }
+    }
+
+    @Test
+    public void testReadSseEventAsPlainString() throws Exception {
+        final Response r = target().path("test/single").request().get(Response.class);
+        assertThat(r.readEntity(String.class), containsString("single"));
+    }
+
+    /**
+     * Reproducer for JERSEY-2912: Sending and receiving comments-only events.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testReadCommentsOnlySseEvents() throws Exception {
+        ClientConfig clientConfig = new ClientConfig();
+        clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 15000);
+        clientConfig.property(ClientProperties.READ_TIMEOUT, 0);
+        clientConfig.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 8);
+        clientConfig.connectorProvider(new GrizzlyConnectorProvider());
+        Client client = ClientBuilder.newBuilder().withConfig(clientConfig).build();
+
+        final CountDownLatch latch = new CountDownLatch(2);
+        final Queue<String> eventComments = new ArrayBlockingQueue<>(2);
+        WebTarget single = client.target(getBaseUri()).path("test/comments-only");
+        EventSource es = EventSource.target(single).build();
+        es.register(new EventListener() {
+            @Override
+            public void onEvent(InboundEvent inboundEvent) {
+                eventComments.add(inboundEvent.getComment());
+                latch.countDown();
+            }
+        });
+
+        boolean latchTimedOut;
+        boolean closeTimedOut;
+        try {
+            es.open();
+            latchTimedOut = latch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        } finally {
+            closeTimedOut = es.close(5, TimeUnit.SECONDS);
+        }
+
+        assertEquals("Unexpected event count", 2, eventComments.size());
+        for (int i = 1; i <= 2; i++) {
+            assertEquals("Unexpected comment data on event #" + i, "No comment #" + i, eventComments.poll());
+        }
+        assertTrue("Event latch has timed out", latchTimedOut);
+        assertTrue("EventSource.close() has timed out", closeTimedOut);
+    }
+
+    @Test
+    public void testReadFromClosedOutput() throws Exception {
+        /**
+         * Need to disable HTTP Keep-Alive to prevent this test from hanging in HttpURLConnection
+         * due to an attempt to read from a stale, out-of-sync connection closed by the server.
+         * Thus setting the "Connection: close" HTTP header on all requests.
+         */
+        Response r;
+        r = target().path("test/closed-empty").request().header("Connection", "close").get();
+        assertTrue(r.readEntity(String.class).isEmpty());
+
+        r = target().path("test/closed-single").request().header("Connection", "close").get();
+        assertTrue(r.readEntity(String.class).contains("closed"));
+
+        //
+
+        EventInput input;
+        input = target().path("test/closed-single").request().header("Connection", "close").get(EventInput.class);
+        assertEquals("closed", input.read().readData());
+        assertEquals(null, input.read());
+        assertTrue(input.isClosed());
+
+        input = target().path("test/closed-empty").request().header("Connection", "close").get(EventInput.class);
+        assertEquals(null, input.read());
+        assertTrue(input.isClosed());
+    }
+
+    @Test
+    public void testSseContentTypeWithCharset() {
+        /**
+         * Need to disable HTTP Keep-Alive to prevent this test from hanging in HttpURLConnection
+         * due to an attempt to read from a stale, out-of-sync connection closed by the server.
+         * Thus setting the "Connection: close" HTTP header on all requests.
+         */
+        Response r;
+        r = target().path("test/charset").request().header("Connection", "close").get();
+        assertTrue(r.getMediaType().getParameters().get("charset").equalsIgnoreCase("utf-8"));
+        final EventInput eventInput = r.readEntity(EventInput.class);
+        String eventData = eventInput.read().readData();
+        assertEquals("charset", eventData);
+        eventInput.close();
+    }
+
+    @Test
+    public void testGrizzlyConnectorWithEventSource() throws InterruptedException {
+        ClientConfig clientConfig = new ClientConfig();
+        clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 15000);
+        clientConfig.property(ClientProperties.READ_TIMEOUT, 0);
+        clientConfig.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 8);
+        clientConfig.connectorProvider(new GrizzlyConnectorProvider());
+        Client client = ClientBuilder.newBuilder().withConfig(clientConfig).build();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AtomicReference<String> eventData = new AtomicReference<String>();
+        final AtomicInteger counter = new AtomicInteger(0);
+        WebTarget single = client.target(getBaseUri()).path("test/single");
+        EventSource es = EventSource.target(single).build();
+        es.register(new EventListener() {
+            @Override
+            public void onEvent(InboundEvent inboundEvent) {
+                final int i = counter.incrementAndGet();
+                if (i == 1) {
+                    eventData.set(inboundEvent.readData());
+                }
+                latch.countDown();
+            }
+        });
+
+        boolean latchTimedOut;
+        boolean closeTimedOut;
+        try {
+            es.open();
+            latchTimedOut = latch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        } finally {
+            closeTimedOut = es.close(5, TimeUnit.SECONDS);
+        }
+
+        assertEquals("Unexpected event count", 1, counter.get());
+        assertEquals("Unexpected event data", "single", eventData.get());
+        assertTrue("Event latch has timed out", latchTimedOut);
+        assertTrue("EventSource.close() has timed out", closeTimedOut);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventSourceWithNamedEventsTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventSourceWithNamedEventsTest.java
new file mode 100644
index 0000000..28f9edf
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventSourceWithNamedEventsTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2013, 2018 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.sse;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.media.sse.EventListener;
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.media.sse.InboundEvent;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests handling of SSEs with name defined in {@link EventSource}.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class EventSourceWithNamedEventsTest extends JerseyTest {
+
+    public static final String SSE_NAME = "message-to-client";
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(SseResource.class, SseFeature.class);
+        return resourceConfig;
+    }
+
+    public static final int MSG_COUNT = 10;
+    private static final CountDownLatch latch = new CountDownLatch(MSG_COUNT);
+
+    @Path("events")
+    @Singleton
+    public static class SseResource {
+
+        @GET
+        @Produces(SseFeature.SERVER_SENT_EVENTS)
+        public EventOutput getServerSentEvents() {
+            final EventOutput eventOutput = new EventOutput();
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        int i = 0;
+                        while (latch.getCount() > 0) {
+
+                            // send message with name "message-to-client" -> should be read by the client
+                            eventOutput.write(new OutboundEvent.Builder()
+                                    .name("message-to-client")
+                                    .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                                    .data(Integer.class, i)
+                                    .build());
+
+                            // send another event with name "foo" -> should be ignored by the client
+                            eventOutput.write(new OutboundEvent.Builder()
+                                    .name("foo")
+                                    .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                                    .data(String.class, "bar")
+                                    .build());
+
+                            // send another un-mamed event -> should be ignored by the client
+                            eventOutput.write(new OutboundEvent.Builder()
+                                    .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                                    .data(String.class, "baz")
+                                    .build());
+                            latch.countDown();
+                            i++;
+                        }
+
+                    } catch (IOException e) {
+                        throw new RuntimeException("Error when writing the event.", e);
+                    } finally {
+                        try {
+                            eventOutput.close();
+                        } catch (IOException ioClose) {
+                            throw new RuntimeException("Error when closing the event output.", ioClose);
+                        }
+                    }
+                }
+            }).start();
+            return eventOutput;
+        }
+    }
+
+
+    @Test
+    public void testWithEventSource() throws IOException, NoSuchAlgorithmException, InterruptedException {
+        final WebTarget endpoint = target().register(SseFeature.class).path("events");
+        EventSource eventSource = EventSource.target(endpoint).build();
+        final CountDownLatch count = new CountDownLatch(MSG_COUNT);
+
+        final EventListener listener = new EventListener() {
+            @Override
+            public void onEvent(InboundEvent inboundEvent) {
+                try {
+                    final Integer data = inboundEvent.readData(Integer.class);
+                    System.out.println(inboundEvent.getName() + "; " + data);
+                    Assert.assertEquals(SSE_NAME, inboundEvent.getName());
+                    Assert.assertEquals(MSG_COUNT - count.getCount(), data.intValue());
+                    count.countDown();
+                } catch (ProcessingException ex) {
+                    throw new RuntimeException("Error when deserializing of data.", ex);
+                }
+            }
+        };
+        eventSource.register(listener, "message-to-client");
+        eventSource.open();
+        final boolean sent = latch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        Assert.assertTrue("Awaiting for SSE message has timeout. Not all message were sent.", sent);
+        final boolean handled = count.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        Assert.assertTrue("Awaiting for SSE message has timeout. Not all message were handled by the listener.", handled);
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/GenericEntityTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/GenericEntityTest.java
new file mode 100644
index 0000000..cbd3e9b
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/GenericEntityTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2018 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.sse;
+
+import javafx.util.Pair;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import javax.inject.Singleton;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseEventSink;
+import javax.ws.rs.sse.SseEventSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+public class GenericEntityTest extends JerseyTest {
+    private static final int BUFFER_SIZE = 20;
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(GenericEntityTest.SSEGenericEntityResource.class, ListPairMBRW.class, PairMBRW.class);
+    }
+
+    @Test
+    public void testGenericString() throws InterruptedException {
+        WebTarget sseTarget = target("genericentityresource/string");
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        MessageLatch<String> messageLatch = new MessageLatch<>(countDownLatch);
+        try (SseEventSource source = SseEventSource.target(sseTarget).build()) {
+            source.register(event -> messageLatch.consume(event.readData()));
+            source.open();
+            assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
+            assertEquals("Cindy", messageLatch.data().get(0));
+        }
+    }
+
+    @Test
+    public void testGenericPair() throws InterruptedException {
+        WebTarget sseTarget = target("genericentityresource/pair").register(PairMBRW.class);
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        MessageLatch<Pair<String, Integer>> messageLatch = new MessageLatch<>(countDownLatch);
+        try (SseEventSource source = SseEventSource.target(sseTarget).build()) {
+            source.register(event -> messageLatch.consume(event.readData(Pair.class)));
+            source.open();
+            assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
+            Pair<String, Integer> pair = messageLatch.data().get(0);
+            assertEquals("Cindy", pair.getKey());
+            assertEquals(30, pair.getValue().intValue());
+        }
+    }
+
+    @Test
+    public void testGenericList() throws InterruptedException {
+        WebTarget sseTarget = target("genericentityresource/list").register(ListPairMBRW.class);
+        CountDownLatch countDownLatch = new CountDownLatch(2);
+        MessageLatch<Pair<String, Integer>> messageLatch = new MessageLatch<>(countDownLatch);
+        try (SseEventSource source = SseEventSource.target(sseTarget).build()) {
+            source.register(event -> messageLatch.consume((List<Pair<String, Integer>>) event.readData(List.class)));
+            source.open();
+            assertTrue(countDownLatch.await(5, TimeUnit.SECONDS));
+            Pair<String, Integer> cindy = messageLatch.data().get(0);
+            Pair<String, Integer> jack = messageLatch.data().get(1);
+            assertEquals("Cindy", cindy.getKey());
+            assertEquals(30, cindy.getValue().intValue());
+            assertEquals("Jack", jack.getKey());
+            assertEquals(32, jack.getValue().intValue());
+        }
+    }
+
+    @Singleton
+    @Path("genericentityresource")
+    public static class SSEGenericEntityResource {
+        @GET
+        @Path("string")
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        public void sendString(@Context SseEventSink sink, @Context Sse sse) {
+            GenericEntity<String> ges = new GenericEntity<String>("Cindy") {
+            };
+            try (SseEventSink s = sink) {
+                s.send(sse.newEventBuilder().data(ges).build());
+            }
+        }
+
+        @GET
+        @Path("pair")
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        public void sendPair(@Context SseEventSink sink, @Context Sse sse) {
+            Pair<String, Integer> person = new Pair<>("Cindy", 30);
+            GenericEntity<Pair<String, Integer>> entity = new GenericEntity<Pair<String, Integer>>(person) {
+            };
+            try (SseEventSink s = sink) {
+                s.send(sse.newEventBuilder().data(entity).build());
+            }
+        }
+
+        @GET
+        @Path("list")
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        public void sendList(@Context SseEventSink sink, @Context Sse sse) {
+            Pair<String, Integer> person1 = new Pair<>("Cindy", 30);
+            Pair<String, Integer> person2 = new Pair<>("Jack", 32);
+            List<Pair<String, Integer>> people = new LinkedList<>();
+            people.add(person1);
+            people.add(person2);
+            GenericEntity<List<Pair<String, Integer>>> entity = new GenericEntity<List<Pair<String, Integer>>>(people) {
+            };
+            try (SseEventSink s = sink) {
+                s.send(sse.newEventBuilder().data(entity).build());
+            }
+        }
+    }
+
+    private static class PairMBRW implements
+            MessageBodyWriter<Pair<String, Integer>>, MessageBodyReader<Pair<String, Integer>> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == Pair.class;
+        }
+
+        @Override
+        public Pair<String, Integer> readFrom(Class<Pair<String, Integer>> type, Type genericType,
+                                              Annotation[] annotations, MediaType mediaType,
+                                              MultivaluedMap<String, String> httpHeaders,
+                                              InputStream entityStream) throws IOException, WebApplicationException {
+            byte[] buffer = new byte[GenericEntityTest.BUFFER_SIZE];
+            entityStream.read(buffer);
+            return readFrom(new String(buffer, Charset.defaultCharset()).trim());
+        }
+
+        static Pair<String, Integer> readFrom(String from) {
+            String[] split = from.split(",", 2);
+            return new Pair<String, Integer>(split[0], Integer.parseInt(split[1]));
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == Pair.class;
+        }
+
+        @Override
+        public void writeTo(Pair<String, Integer> stringIntegerPair, Class<?> type, Type genericType,
+                            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            writeTo(stringIntegerPair, entityStream);
+        }
+
+        static void writeTo(Pair<String, Integer> stringIntegerPair, OutputStream entityStream) throws IOException {
+            StringBuilder sb = new StringBuilder();
+            sb.append(stringIntegerPair.getKey()).append(",").append(stringIntegerPair.getValue());
+            entityStream.write(sb.toString().getBytes(Charset.defaultCharset()));
+        }
+    }
+
+    private static class ListPairMBRW implements MessageBodyWriter<List<Pair<String, Integer>>>,
+            MessageBodyReader<List<Pair<String, Integer>>> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == List.class;
+        }
+
+        @Override
+        public List<Pair<String, Integer>> readFrom(Class<List<Pair<String, Integer>>> type, Type genericType,
+                                                    Annotation[] annotations, MediaType mediaType,
+                                                    MultivaluedMap<String, String> httpHeaders,
+                                                    InputStream entityStream)
+                throws IOException, WebApplicationException {
+            List<Pair<String, Integer>> list = new LinkedList<>();
+            byte[] buffer = new byte[20];
+            entityStream.read(buffer);
+            StringTokenizer st = new StringTokenizer(new String(buffer, Charset.defaultCharset()).trim(), ";", false);
+            while (st.hasMoreTokens()) {
+                list.add(PairMBRW.readFrom(st.nextToken()));
+            }
+            return list;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == List.class;
+        }
+
+        @Override
+        public void writeTo(List<Pair<String, Integer>> pairs, Class<?> type, Type genericType,
+                            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            for (Pair<String, Integer> pair : pairs) {
+                PairMBRW.writeTo(pair, entityStream);
+                entityStream.write(";".getBytes());
+            }
+        }
+    }
+
+    private static class MessageLatch<T> {
+        private CountDownLatch countDownLatch;
+        private List<T> data = new LinkedList<>();
+
+        private MessageLatch(CountDownLatch countDownLatch) {
+            this.countDownLatch = countDownLatch;
+        }
+
+        private void consume(List<T> list) {
+            for (T o : list) {
+                data.add(o);
+                countDownLatch.countDown();
+            }
+        }
+
+        private void consume(T o) {
+            data.add(o);
+            countDownLatch.countDown();
+        }
+
+        private List<T> data() {
+            return data;
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseCustomEventImplTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseCustomEventImplTest.java
new file mode 100644
index 0000000..ec58d06
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseCustomEventImplTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017, 2018 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.sse;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.InboundSseEvent;
+import javax.ws.rs.sse.OutboundSseEvent;
+import javax.ws.rs.sse.SseEventSink;
+import javax.ws.rs.sse.SseEventSource;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.media.sse.EventListener;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test that {@link org.glassfish.jersey.media.sse.OutboundEventWriter} works with custom
+ * {@link javax.ws.rs.sse.OutboundSseEvent} implementation.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class SseCustomEventImplTest extends JerseyTest {
+
+    private static final String SSE_EVENT_NAME = "custom-message";
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SseResource.class);
+    }
+
+    @Path("events")
+    @Singleton
+    public static class SseResource {
+
+        @GET
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        public void getServerSentEvents(@Context final SseEventSink eventSink) {
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            executor.submit(() -> {
+                eventSink.send(new MyOutboundEvent("foo"));
+                eventSink.send(new MyOutboundEvent("bar"));
+                eventSink.send(new MyOutboundEvent("baz"));
+            });
+        }
+    }
+
+    @Test
+    public void testWithJaxRsAPI() {
+        final WebTarget endpoint = target().path("events");
+        final List<InboundSseEvent> results = new ArrayList<>();
+        try (final SseEventSource eventSource = SseEventSource.target(endpoint).build()) {
+            final CountDownLatch receivedLatch = new CountDownLatch(3);
+            eventSource.register((event) -> {
+                results.add(event);
+                receivedLatch.countDown();
+            });
+
+            eventSource.open();
+            final boolean allReceived = receivedLatch.await(5000, TimeUnit.MILLISECONDS);
+            Assert.assertTrue(allReceived);
+            Assert.assertEquals(3, results.size());
+            Assert.assertEquals("foo", results.get(0).readData());
+            Assert.assertEquals("bar", results.get(1).readData());
+            Assert.assertEquals("baz", results.get(2).readData());
+        } catch (final InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testWithJerseyAPI() throws InterruptedException {
+        final WebTarget endpoint = target().path("events");
+        final EventSource eventSource = EventSource.target(endpoint).build();
+        final CountDownLatch receiveLatch = new CountDownLatch(3);
+
+        final List<String> results = new ArrayList<>();
+        final EventListener listener = inboundEvent -> {
+            try {
+                results.add(inboundEvent.readData());
+                receiveLatch.countDown();
+                Assert.assertEquals(SSE_EVENT_NAME, inboundEvent.getName());
+            } catch (ProcessingException ex) {
+                throw new RuntimeException("Error when deserializing of data.", ex);
+            }
+        };
+        eventSource.register(listener, SSE_EVENT_NAME);
+        eventSource.open();
+        Assert.assertTrue(receiveLatch.await(5000, TimeUnit.MILLISECONDS));
+        Assert.assertEquals(3, results.size());
+        Assert.assertEquals("foo", results.get(0));
+        Assert.assertEquals("bar", results.get(1));
+        Assert.assertEquals("baz", results.get(2));
+    }
+
+    static class MyOutboundEvent implements OutboundSseEvent {
+
+        private String data;
+
+        public MyOutboundEvent(String data) {
+            this.data = data;
+        }
+
+        @Override
+        public Class<?> getType() {
+            return String.class;
+        }
+
+        @Override
+        public Type getGenericType() {
+            return String.class;
+        }
+
+        @Override
+        public MediaType getMediaType() {
+            return MediaType.TEXT_PLAIN_TYPE;
+        }
+
+        @Override
+        public String getData() {
+            return data;
+        }
+
+        @Override
+        public String getId() {
+            return null;
+        }
+
+        @Override
+        public String getName() {
+            return SSE_EVENT_NAME;
+        }
+
+        @Override
+        public String getComment() {
+            return "";
+        }
+
+        @Override
+        public long getReconnectDelay() {
+            return 0;
+        }
+
+        @Override
+        public boolean isReconnectDelaySet() {
+            return false;
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseEventSinkToEventSourceTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseEventSinkToEventSourceTest.java
new file mode 100644
index 0000000..4bc9af3
--- /dev/null
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseEventSinkToEventSourceTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2017, 2018 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.sse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.sse.InboundSseEvent;
+import javax.ws.rs.sse.Sse;
+import javax.ws.rs.sse.SseEventSink;
+import javax.ws.rs.sse.SseEventSource;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.media.sse.EventListener;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * JAX-RS {@link SseEventSource} and {@link SseEventSink} test.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class SseEventSinkToEventSourceTest extends JerseyTest {
+
+    private static final String INTEGER_SSE_NAME = "integer-message";
+    private static final Logger LOGGER = Logger.getLogger(SseEventSinkToEventSourceTest.class.getName());
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SseResource.class);
+    }
+
+    private static final int MSG_COUNT = 10;
+    private static volatile CountDownLatch transmitLatch;
+
+    @Path("events")
+    @Singleton
+    public static class SseResource {
+
+        @GET
+        @Produces(MediaType.SERVER_SENT_EVENTS)
+        public void getServerSentEvents(@Context final SseEventSink eventSink, @Context final Sse sse) {
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            executor.submit(() -> {
+                int i = 0;
+                while (transmitLatch.getCount() > 0) {
+                    eventSink.send(sse.newEventBuilder()
+                            .name(INTEGER_SSE_NAME)
+                            .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                            .data(Integer.class, i)
+                            .build());
+
+                    // send another event with name "foo" -> should be ignored by the client
+                    eventSink.send(sse.newEventBuilder()
+                            .name("foo")
+                            .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                            .data(String.class, "bar")
+                            .build());
+
+                    // send another unnamed event -> should be ignored by the client
+                    eventSink.send(sse.newEventBuilder()
+                            .mediaType(MediaType.TEXT_PLAIN_TYPE)
+                            .data(String.class, "baz")
+                            .build());
+                    transmitLatch.countDown();
+                    i++;
+                }
+            });
+        }
+    }
+
+    @Test
+    public void testWithSimpleSubscriber() {
+        transmitLatch = new CountDownLatch(MSG_COUNT);
+        final WebTarget endpoint = target().path("events");
+        final List<InboundSseEvent> results = new ArrayList<>();
+        try (final SseEventSource eventSource = SseEventSource.target(endpoint).build()) {
+            final CountDownLatch receivedLatch = new CountDownLatch(3 * MSG_COUNT);
+            eventSource.register((event) -> {
+                results.add(event);
+                receivedLatch.countDown();
+            });
+
+            eventSource.open();
+            final boolean allTransmitted = transmitLatch.await(5000, TimeUnit.MILLISECONDS);
+            final boolean allReceived = receivedLatch.await(5000, TimeUnit.MILLISECONDS);
+            Assert.assertTrue(allTransmitted);
+            Assert.assertTrue(allReceived);
+            Assert.assertEquals(30, results.size());
+        } catch (final InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void testWithJerseyApi() throws InterruptedException {
+        final WebTarget endpoint = target().path("events");
+        final EventSource eventSource = EventSource.target(endpoint).build();
+        transmitLatch = new CountDownLatch(MSG_COUNT);
+        final CountDownLatch receiveLatch = new CountDownLatch(MSG_COUNT);
+
+        final List<Integer> results = new ArrayList<>();
+        final EventListener listener = inboundEvent -> {
+            try {
+                results.add(inboundEvent.readData(Integer.class));
+                receiveLatch.countDown();
+                Assert.assertEquals(INTEGER_SSE_NAME, inboundEvent.getName());
+            } catch (ProcessingException ex) {
+                throw new RuntimeException("Error when deserializing of data.", ex);
+            }
+        };
+        eventSource.register(listener, INTEGER_SSE_NAME);
+        eventSource.open();
+        Assert.assertTrue(transmitLatch.await(5000, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(receiveLatch.await(5000, TimeUnit.MILLISECONDS));
+        Assert.assertEquals(10, results.size());
+    }
+
+
+    @Test
+    public void testWithEventSource() throws InterruptedException {
+        transmitLatch = new CountDownLatch(2 * MSG_COUNT);
+        final WebTarget endpoint = target().path("events");
+        final SseEventSource eventSource = SseEventSource.target(endpoint).build();
+
+        final CountDownLatch count1 = new CountDownLatch(3 * MSG_COUNT);
+        final CountDownLatch count2 = new CountDownLatch(3 * MSG_COUNT);
+
+        eventSource.register(new InboundHandler("consumer1", count1));
+        eventSource.register(new InboundHandler("consumer2", count2));
+
+        eventSource.open();
+        final boolean sent = transmitLatch.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        Assert.assertTrue("Awaiting for SSE message has timeout. Not all message were sent.", sent);
+
+        final boolean handled2 = count2.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        Assert.assertTrue(
+                "Awaiting for SSE message has timeout. Not all message were handled by eventSource2.", handled2);
+
+        final boolean handled1 = count1.await(5 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS);
+        Assert.assertTrue(
+                "Awaiting for SSE message has timeout. Not all message were handled by eventSource1.", handled1);
+
+    }
+
+    private class InboundHandler implements Consumer<InboundSseEvent> {
+        private final CountDownLatch latch;
+        private final String name;
+
+        InboundHandler(final String name, final CountDownLatch latch) {
+            this.latch = latch;
+            this.name = name;
+        }
+
+        @Override
+        public void accept(final InboundSseEvent inboundSseEvent) {
+            try {
+                if (INTEGER_SSE_NAME.equals(inboundSseEvent.getName())) {
+                    final Integer data = inboundSseEvent.readData(Integer.class);
+                    LOGGER.info(String.format("[%s] Integer data received: [id=%s name=%s comment=%s reconnectDelay=%d value=%d]",
+                            name,
+                            inboundSseEvent.getId(),
+                            inboundSseEvent.getName(), inboundSseEvent.getComment(), inboundSseEvent.getReconnectDelay(), data));
+                } else {
+                    final String data = inboundSseEvent.readData();
+                    LOGGER.info(String.format("[%s] String data received: [id=%s name=%s comment=%s reconnectDelay=%d value=%s]",
+                            name,
+                            inboundSseEvent.getId(),
+                            inboundSseEvent.getName(), inboundSseEvent.getComment(), inboundSseEvent.getReconnectDelay(), data));
+                }
+                latch.countDown();
+            } catch (final ProcessingException ex) {
+                throw new RuntimeException("Error when deserializing the data.", ex);
+            }
+        }
+    }
+}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/Jersey_yellow.png b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/Jersey_yellow.png
new file mode 100644
index 0000000..ec74cd2
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/Jersey_yellow.png
Binary files differ
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/duke_rocket.gif b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/duke_rocket.gif
new file mode 100644
index 0000000..420304f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/duke_rocket.gif
Binary files differ
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/multipart-testcase.txt b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/multipart-testcase.txt
new file mode 100644
index 0000000..e4557cc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/multipart-testcase.txt
@@ -0,0 +1,11 @@
+-----------------------------7dc941520888
+Content-Disposition: form-data; name="text"
+
+bhhklb
+-----------------------------7dc941520888
+Content-Disposition: form-data; name="file"; filename="C:\java\projects\multipart-testcase\pom.xml"
+Content-Type: text/xml
+
+<project/>
+
+-----------------------------7dc941520888--
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt
new file mode 100644
index 0000000..fad27b5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt
@@ -0,0 +1 @@
+COMPROMISED
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList.json
new file mode 100644
index 0000000..e91caf6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"Dog","name":"Fifi"},{"@type":"Cat","name":"Daisy","nickName":null}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList_MOXy.json
new file mode 100644
index 0000000..e91caf6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"Dog","name":"Fifi"},{"@type":"Cat","name":"Daisy","nickName":null}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean.json
new file mode 100644
index 0000000..dfb3702
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean_MOXy.json
new file mode 100644
index 0000000..dfb3702
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean_MOXy.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean_MOXy.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes.json
new file mode 100644
index 0000000..892c41c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"},{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2.json
new file mode 100644
index 0000000..0a3dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
new file mode 100644
index 0000000..0a3dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4.json
new file mode 100644
index 0000000..b654a20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4.json
@@ -0,0 +1 @@
+{"a1":null,"a2":null,"list":[],"b":{"uri":null,"s1":null,"i":null,"j":null}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
new file mode 100644
index 0000000..b654a20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
@@ -0,0 +1 @@
+{"a1":null,"a2":null,"list":[],"b":{"uri":null,"s1":null,"i":null,"j":null}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..892c41c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"},{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean.json
new file mode 100644
index 0000000..440cf2e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean.json
@@ -0,0 +1 @@
+{"nullOnly":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean_MOXy.json
new file mode 100644
index 0000000..440cf2e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean_MOXy.json
@@ -0,0 +1 @@
+{"nullOnly":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean.json
new file mode 100644
index 0000000..10a199b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean.json
@@ -0,0 +1 @@
+{"emptyBean":null,"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean_MOXy.json
new file mode 100644
index 0000000..10a199b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean_MOXy.json
@@ -0,0 +1 @@
+{"emptyBean":null,"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean_MOXy.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean_MOXy.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean.json
new file mode 100644
index 0000000..f32b098
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean.json
@@ -0,0 +1 @@
+{"weight":["1kg","2kg"],"color":"red","name":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean_MOXy.json
new file mode 100644
index 0000000..f32b098
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean_MOXy.json
@@ -0,0 +1 @@
+{"weight":["1kg","2kg"],"color":"red","name":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray_MOXy.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray_MOXy.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List.json
new file mode 100644
index 0000000..75b2873
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List.json
@@ -0,0 +1 @@
+{"objects":[{"@type":"ColorHolder","colors":["RED","BLUE"]},{"@type":"ColorHolder","colors":["GREEN"]}],"offset":0,"total":2}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List_MOXy.json
new file mode 100644
index 0000000..75b2873
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List_MOXy.json
@@ -0,0 +1 @@
+{"objects":[{"@type":"ColorHolder","colors":["RED","BLUE"]},{"@type":"ColorHolder","colors":["GREEN"]}],"offset":0,"total":2}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean_MOXy.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean_MOXy.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean_MOXy.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean_MOXy.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean.json
new file mode 100644
index 0000000..379160e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean.json
@@ -0,0 +1 @@
+{"property":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean_MOXy.json
new file mode 100644
index 0000000..379160e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse.json
new file mode 100644
index 0000000..619148e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse.json
@@ -0,0 +1 @@
+{"myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}],"myError":{"id":"-1","desc":"error"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse_MOXy.json
new file mode 100644
index 0000000..619148e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse_MOXy.json
@@ -0,0 +1 @@
+{"myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}],"myError":{"id":"-1","desc":"error"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean.json
new file mode 100644
index 0000000..0386d5d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean.json
@@ -0,0 +1 @@
+{"a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute.json
new file mode 100644
index 0000000..a23e9d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute.json
@@ -0,0 +1 @@
+{"attr":"value","a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
new file mode 100644
index 0000000..a23e9d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
@@ -0,0 +1 @@
+{"attr":"value","a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean_MOXy.json
new file mode 100644
index 0000000..0386d5d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean_MOXy.json
@@ -0,0 +1 @@
+{"a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean_MOXy.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean_MOXy.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person.json
new file mode 100644
index 0000000..f3dc062
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person.json
@@ -0,0 +1 @@
+{"name":"Joe Schmo","children":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person_MOXy.json
new file mode 100644
index 0000000..f3dc062
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person_MOXy.json
@@ -0,0 +1 @@
+{"name":"Joe Schmo","children":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList.json
new file mode 100644
index 0000000..e3f81df
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"PojoDog","name":"Fifi"},{"@type":"PojoCat","name":"Daisy","nickName":null}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList_MOXy.json
new file mode 100644
index 0000000..e3f81df
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"PojoDog","name":"Fifi"},{"@type":"PojoCat","name":"Daisy","nickName":null}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean.json
new file mode 100644
index 0000000..13d3153
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean.json
@@ -0,0 +1 @@
+{"value":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean_MOXy.json
new file mode 100644
index 0000000..13d3153
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"value":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage_MOXy.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage_MOXy.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes.json
new file mode 100644
index 0000000..5868341
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..5868341
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean_MOXy.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean_MOXy.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel.json
new file mode 100644
index 0000000..7ffadb3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel.json
@@ -0,0 +1 @@
+{"root":{"label":"dummy node","expanded":false}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel_MOXy.json
new file mode 100644
index 0000000..7ffadb3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel_MOXy.json
@@ -0,0 +1 @@
+{"root":{"label":"dummy node","expanded":false}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean_MOXy.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User.json
new file mode 100644
index 0000000..5032ee5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User.json
@@ -0,0 +1 @@
+{"name":"Grotefend","userid":"1621"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable.json
new file mode 100644
index 0000000..7c9561f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"name":"Grotefend","userid":"1621"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable_MOXy.json
new file mode 100644
index 0000000..7c9561f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable_MOXy.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"name":"Grotefend","userid":"1621"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User_MOXy.json
new file mode 100644
index 0000000..5032ee5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User_MOXy.json
@@ -0,0 +1 @@
+{"name":"Grotefend","userid":"1621"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList.json
new file mode 100644
index 0000000..3da277e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"Dog","name":"Fifi"},{"@type":"Cat","nickName":null,"name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList_MOXy.json
new file mode 100644
index 0000000..368ad25
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"Dog","name":"Fifi"},{"@type":"Cat","name":"Daisy","nickName":null}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean.json
new file mode 100644
index 0000000..f364f00
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean.json
@@ -0,0 +1 @@
+{"prop":"testProp","cats":[{"nickName":"Kitty","name":"Foo"},{"nickName":"Puss","name":"Bar"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean_MOXy.json
new file mode 100644
index 0000000..d9dc02f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean_MOXy.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean_MOXy.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes.json
new file mode 100644
index 0000000..892c41c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"},{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2.json
new file mode 100644
index 0000000..0a3dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
new file mode 100644
index 0000000..0a3dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4.json
new file mode 100644
index 0000000..b654a20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4.json
@@ -0,0 +1 @@
+{"a1":null,"a2":null,"list":[],"b":{"uri":null,"s1":null,"i":null,"j":null}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
new file mode 100644
index 0000000..01a7674
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
@@ -0,0 +1 @@
+{"a1":null,"a2":null,"filler1":null,"list":[],"filler2":null,"b":{"uri":null,"s1":null,"i":null,"j":null}}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..892c41c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"},{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean.json
new file mode 100644
index 0000000..440cf2e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean.json
@@ -0,0 +1 @@
+{"nullOnly":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean_MOXy.json
new file mode 100644
index 0000000..440cf2e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean_MOXy.json
@@ -0,0 +1 @@
+{"nullOnly":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean.json
new file mode 100644
index 0000000..10a199b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean.json
@@ -0,0 +1 @@
+{"emptyBean":null,"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean_MOXy.json
new file mode 100644
index 0000000..10a199b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean_MOXy.json
@@ -0,0 +1 @@
+{"emptyBean":null,"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean_MOXy.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean_MOXy.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean.json
new file mode 100644
index 0000000..f32b098
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean.json
@@ -0,0 +1 @@
+{"weight":["1kg","2kg"],"color":"red","name":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean_MOXy.json
new file mode 100644
index 0000000..f32b098
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean_MOXy.json
@@ -0,0 +1 @@
+{"weight":["1kg","2kg"],"color":"red","name":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray_MOXy.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray_MOXy.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List.json
new file mode 100644
index 0000000..75b2873
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List.json
@@ -0,0 +1 @@
+{"objects":[{"@type":"ColorHolder","colors":["RED","BLUE"]},{"@type":"ColorHolder","colors":["GREEN"]}],"offset":0,"total":2}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List_MOXy.json
new file mode 100644
index 0000000..75b2873
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List_MOXy.json
@@ -0,0 +1 @@
+{"objects":[{"@type":"ColorHolder","colors":["RED","BLUE"]},{"@type":"ColorHolder","colors":["GREEN"]}],"offset":0,"total":2}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean_MOXy.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean_MOXy.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean_MOXy.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean_MOXy.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean.json
new file mode 100644
index 0000000..379160e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean.json
@@ -0,0 +1 @@
+{"property":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean_MOXy.json
new file mode 100644
index 0000000..379160e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse.json
new file mode 100644
index 0000000..619148e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse.json
@@ -0,0 +1 @@
+{"myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}],"myError":{"id":"-1","desc":"error"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse_MOXy.json
new file mode 100644
index 0000000..619148e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse_MOXy.json
@@ -0,0 +1 @@
+{"myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}],"myError":{"id":"-1","desc":"error"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean.json
new file mode 100644
index 0000000..0386d5d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean.json
@@ -0,0 +1 @@
+{"a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute.json
new file mode 100644
index 0000000..a23e9d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute.json
@@ -0,0 +1 @@
+{"attr":"value","a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
new file mode 100644
index 0000000..a23e9d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
@@ -0,0 +1 @@
+{"attr":"value","a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean_MOXy.json
new file mode 100644
index 0000000..0386d5d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean_MOXy.json
@@ -0,0 +1 @@
+{"a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean_MOXy.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean_MOXy.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person.json
new file mode 100644
index 0000000..466eda7
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person.json
@@ -0,0 +1 @@
+{"name":"Joe Schmo","child":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person_MOXy.json
new file mode 100644
index 0000000..b96eb26
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person_MOXy.json
@@ -0,0 +1 @@
+{"name":"Joe Schmo","child":[{"name":"Jill Schmo","child":null},{"name":"Jack Schmo","child":null}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList.json
new file mode 100644
index 0000000..2a49520
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"PojoDog","name":"Fifi"},{"@type":"PojoCat","nickName":null,"name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList_MOXy.json
new file mode 100644
index 0000000..2387979
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"PojoDog","name":"Fifi"},{"@type":"PojoCat","name":"Daisy","nickName":null}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean.json
new file mode 100644
index 0000000..13d3153
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean.json
@@ -0,0 +1 @@
+{"value":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean_MOXy.json
new file mode 100644
index 0000000..13d3153
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"value":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage_MOXy.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage_MOXy.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes.json
new file mode 100644
index 0000000..5868341
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..5868341
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","s1":"hi there","i":312,"j":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean_MOXy.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean_MOXy.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel.json
new file mode 100644
index 0000000..7ffadb3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel.json
@@ -0,0 +1 @@
+{"root":{"label":"dummy node","expanded":false}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel_MOXy.json
new file mode 100644
index 0000000..92e0027
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel_MOXy.json
@@ -0,0 +1 @@
+{"root":{"label":"dummy node","expanded":false,"children":null}}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean_MOXy.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User.json
new file mode 100644
index 0000000..a1b91b6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User.json
@@ -0,0 +1 @@
+{"userid":"1621","name":"Grotefend"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable.json
new file mode 100644
index 0000000..2652195
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"userid":"1621","name":"Grotefend"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable_MOXy.json
new file mode 100644
index 0000000..3c38fcb
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable_MOXy.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"name":"Grotefend","userid":"1621"}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User_MOXy.json
new file mode 100644
index 0000000..a517e84
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User_MOXy.json
@@ -0,0 +1 @@
+{"name":"Grotefend","userid":"1621"}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList.json
new file mode 100644
index 0000000..97dda24
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList.json
@@ -0,0 +1 @@
+{"animalList":{"animals":[{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"dog","name":{"$":"Fifi"}},{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"cat","name":{"$":"Daisy"}}]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList_MOXy.json
new file mode 100644
index 0000000..97dda24
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animalList":{"animals":[{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"dog","name":{"$":"Fifi"}},{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"cat","name":{"$":"Daisy"}}]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean.json
new file mode 100644
index 0000000..b4d148e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean.json
@@ -0,0 +1 @@
+{"anotherArrayTestBean":{"cats":[{"name":{"$":"Foo"},"nickName":{"$":"Kitty"}},{"name":{"$":"Bar"},"nickName":{"$":"Puss"}}],"prop":{"$":"testProp"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean_MOXy.json
new file mode 100644
index 0000000..b4d148e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean_MOXy.json
@@ -0,0 +1 @@
+{"anotherArrayTestBean":{"cats":[{"name":{"$":"Foo"},"nickName":{"$":"Kitty"}},{"name":{"$":"Bar"},"nickName":{"$":"Puss"}}],"prop":{"$":"testProp"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean.json
new file mode 100644
index 0000000..4b41cc9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean.json
@@ -0,0 +1 @@
+{"parent":{"@attr":"aval","$":"pval"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean_MOXy.json
new file mode 100644
index 0000000..4b41cc9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"parent":{"@attr":"aval","$":"pval"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes.json
new file mode 100644
index 0000000..32bb5fe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes":{"@a2":"31415926","@a1":"hello dolly","filler1":{"$":"111"},"list":[{"@j":"bumper","@i":"312","@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","s1":{"$":"hi there"}},{"@j":"bumper","@i":"312","@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","s1":{"$":"hi there"}}],"filler2":{"$":"222"},"b":{"@j":"bumper","@i":"312","@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","s1":{"$":"hi there"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2.json
new file mode 100644
index 0000000..ff8b049
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes2":{"@a2":"31415926","@a1":"hello dolly","filler1":{"$":"111"},"list":[{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"},{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}],"filler2":{"$":"222"},"b":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
new file mode 100644
index 0000000..b9e68ae
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes2":{"@a1":"hello dolly","@a2":"31415926","filler1":{"$":"111"},"list":[{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"},{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}],"filler2":{"$":"222"},"b":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3.json
new file mode 100644
index 0000000..9f6d6ca
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes3":{"b":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"},"c":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
new file mode 100644
index 0000000..9f6d6ca
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes3":{"b":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"},"c":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4.json
new file mode 100644
index 0000000..3199988
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes4":{"b":{}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
new file mode 100644
index 0000000..3199988
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes4":{"b":{}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..b1aac1c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes":{"@a1":"hello dolly","@a2":"31415926","filler1":{"$":"111"},"list":[{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","@i":"312","@j":"bumper","s1":{"$":"hi there"}},{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","@i":"312","@j":"bumper","s1":{"$":"hi there"}}],"filler2":{"$":"222"},"b":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","@i":"312","@j":"bumper","s1":{"$":"hi there"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean.json
new file mode 100644
index 0000000..87fb1f6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean.json
@@ -0,0 +1 @@
+{"emptyElementBean":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean_MOXy.json
new file mode 100644
index 0000000..87fb1f6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean_MOXy.json
@@ -0,0 +1 @@
+{"emptyElementBean":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean.json
new file mode 100644
index 0000000..6c111ff
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean.json
@@ -0,0 +1 @@
+{"emptyElementContainingBean":{"c":{"$":"foo"},"d":{"$":"bar"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean_MOXy.json
new file mode 100644
index 0000000..6c111ff
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean_MOXy.json
@@ -0,0 +1 @@
+{"emptyElementContainingBean":{"c":{"$":"foo"},"d":{"$":"bar"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean.json
new file mode 100644
index 0000000..e3c9100
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean.json
@@ -0,0 +1 @@
+{"encodedContentBean":{"one":{"$":"\tone\n\tbig"},"two":{"$":"hafČ"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean_MOXy.json
new file mode 100644
index 0000000..e3c9100
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean_MOXy.json
@@ -0,0 +1 @@
+{"encodedContentBean":{"one":{"$":"\tone\n\tbig"},"two":{"$":"hafČ"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean.json
new file mode 100644
index 0000000..ac8f046
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean.json
@@ -0,0 +1 @@
+{"fakeArrayBean":{"weight":[{"$":"1kg"},{"$":"2kg"}],"color":{"$":"red"},"name":{"$":"bumper"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean_MOXy.json
new file mode 100644
index 0000000..ac8f046
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean_MOXy.json
@@ -0,0 +1 @@
+{"fakeArrayBean":{"weight":[{"$":"1kg"},{"$":"2kg"}],"color":{"$":"red"},"name":{"$":"bumper"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray.json
new file mode 100644
index 0000000..2e64388
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray.json
@@ -0,0 +1 @@
+{"intArray":{"intArray":{"$":"4"},"integerArray":{"$":"3"},"number":{"$":"8"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray_MOXy.json
new file mode 100644
index 0000000..2e64388
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray_MOXy.json
@@ -0,0 +1 @@
+{"intArray":{"intArray":{"$":"4"},"integerArray":{"$":"3"},"number":{"$":"8"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List.json
new file mode 100644
index 0000000..c44dcdc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List.json
@@ -0,0 +1 @@
+{"jersey1199List":{"objects":[{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"colorHolder","colors":[{"$":"RED"},{"$":"BLUE"}]},{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"colorHolder","colors":{"$":"GREEN"}}],"offset":{"$":"0"},"total":{"$":"2"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List_MOXy.json
new file mode 100644
index 0000000..c44dcdc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List_MOXy.json
@@ -0,0 +1 @@
+{"jersey1199List":{"objects":[{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"colorHolder","colors":[{"$":"RED"},{"$":"BLUE"}]},{"@xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"@xsi:type":"colorHolder","colors":{"$":"GREEN"}}],"offset":{"$":"0"},"total":{"$":"2"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean.json
new file mode 100644
index 0000000..91e8e27
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean.json
@@ -0,0 +1 @@
+{"listAndNonListBean":{"a":{"$":"1"},"d":{"$":"2"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean_MOXy.json
new file mode 100644
index 0000000..91e8e27
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean_MOXy.json
@@ -0,0 +1 @@
+{"listAndNonListBean":{"a":{"$":"1"},"d":{"$":"2"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean.json
new file mode 100644
index 0000000..0d9d148
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean.json
@@ -0,0 +1 @@
+{"listEmptyBean":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean_MOXy.json
new file mode 100644
index 0000000..0d9d148
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean_MOXy.json
@@ -0,0 +1 @@
+{"listEmptyBean":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean.json
new file mode 100644
index 0000000..bee9df5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean.json
@@ -0,0 +1 @@
+{"item":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean_MOXy.json
new file mode 100644
index 0000000..bee9df5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"item":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse.json
new file mode 100644
index 0000000..9c4baca
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse.json
@@ -0,0 +1 @@
+{"myResponse":{"@xmlns":{"ns2":"http:\/\/test.jaxb.com"},"ns2:myMessage":[{"id":{"$":"0"},"text":{"$":"ok"}},{"id":{"$":"1"},"text":{"$":"ok"}}],"ns2:myError":{"id":{"$":"-1"},"desc":{"$":"error"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse_MOXy.json
new file mode 100644
index 0000000..575635e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse_MOXy.json
@@ -0,0 +1 @@
+{"myResponse":{"@xmlns":{"ns0":"http:\/\/test.jaxb.com"},"ns0:myMessage":[{"id":{"$":"0"},"text":{"$":"ok"}},{"id":{"$":"1"},"text":{"$":"ok"}}],"ns0:myError":{"id":{"$":"-1"},"desc":{"$":"error"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean.json
new file mode 100644
index 0000000..9affde6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean.json
@@ -0,0 +1 @@
+{"namespaceBean":{"@xmlns":{"ns2":"http:\/\/example.com"},"a":{"$":"foo"},"ns2:b":{"$":"bar"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute.json
new file mode 100644
index 0000000..0287db4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute.json
@@ -0,0 +1 @@
+{"namespaceBeanWithAttribute":{"@xmlns":{"ns1":"http:\/\/example.com"},"@ns1:attr":"value","a":{"$":"foo"},"ns1:b":{"$":"bar"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
new file mode 100644
index 0000000..9223397
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
@@ -0,0 +1 @@
+{"namespaceBeanWithAttribute":{"@xmlns":{"ns0":"http:\/\/example.com"},"@ns0:attr":"value","a":{"$":"foo"},"ns0:b":{"$":"bar"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean_MOXy.json
new file mode 100644
index 0000000..74942c4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean_MOXy.json
@@ -0,0 +1 @@
+{"namespaceBean":{"@xmlns":{"ns0":"http:\/\/example.com"},"a":{"$":"foo"},"ns0:b":{"$":"bar"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean.json
new file mode 100644
index 0000000..91eb8da
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean.json
@@ -0,0 +1 @@
+{"nullStringBean":{"nullString":{"$":"not null to test if set to null works"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean_MOXy.json
new file mode 100644
index 0000000..91eb8da
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean_MOXy.json
@@ -0,0 +1 @@
+{"nullStringBean":{"nullString":{"$":"not null to test if set to null works"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person.json
new file mode 100644
index 0000000..3196a22
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person.json
@@ -0,0 +1 @@
+{"person":{"name":{"$":"Joe Schmo"},"children":{"child":[{"name":{"$":"Jill Schmo"}},{"name":{"$":"Jack Schmo"}}]}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person_MOXy.json
new file mode 100644
index 0000000..3196a22
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person_MOXy.json
@@ -0,0 +1 @@
+{"person":{"name":{"$":"Joe Schmo"},"children":{"child":[{"name":{"$":"Jill Schmo"}},{"name":{"$":"Jack Schmo"}}]}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean.json
new file mode 100644
index 0000000..ff098bc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean.json
@@ -0,0 +1 @@
+{"pureCharDataBean":{"$":"some textual content"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean_MOXy.json
new file mode 100644
index 0000000..ff098bc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"pureCharDataBean":{"$":"some textual content"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage.json
new file mode 100644
index 0000000..308b42b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage.json
@@ -0,0 +1 @@
+{"registerMessage":{"@requestTime":"1234","@agentUID":"agentKocka"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage_MOXy.json
new file mode 100644
index 0000000..d288d49
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage_MOXy.json
@@ -0,0 +1 @@
+{"registerMessage":{"@agentUID":"agentKocka","@requestTime":"1234"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean.json
new file mode 100644
index 0000000..fb691d8
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean.json
@@ -0,0 +1 @@
+{"DummyOne":{"child":{"$":"simple"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes.json
new file mode 100644
index 0000000..e044067
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes.json
@@ -0,0 +1 @@
+{"simpleBeanWithAttributes":{"@j":"bumper","@i":"312","@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","s1":{"$":"hi there"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..711b962
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"simpleBeanWithAttributes":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","@i":"312","@j":"bumper","s1":{"$":"hi there"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute.json
new file mode 100644
index 0000000..e2ec3e2
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttribute":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
new file mode 100644
index 0000000..91166c6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttributeAndValue":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","$":"characters"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
new file mode 100644
index 0000000..91166c6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttributeAndValue":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/","$":"characters"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
new file mode 100644
index 0000000..e2ec3e2
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttribute":{"@uri":"http:\/\/localhost:8080\/jedna\/bedna\/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean_MOXy.json
new file mode 100644
index 0000000..fb691d8
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean_MOXy.json
@@ -0,0 +1 @@
+{"DummyOne":{"child":{"$":"simple"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel.json
new file mode 100644
index 0000000..b9b6473
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel.json
@@ -0,0 +1 @@
+{"treeModel":{"root":{"label":{"$":"dummy node"},"expanded":{"$":"false"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel_MOXy.json
new file mode 100644
index 0000000..b9b6473
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel_MOXy.json
@@ -0,0 +1 @@
+{"treeModel":{"root":{"label":{"$":"dummy node"},"expanded":{"$":"false"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean.json
new file mode 100644
index 0000000..a06cb37
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean.json
@@ -0,0 +1 @@
+{"item2":{"property1":[{"$":"a1"},{"$":"a1"}],"property2":{"$":"b1"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean_MOXy.json
new file mode 100644
index 0000000..a06cb37
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"item2":{"property1":[{"$":"a1"},{"$":"a1"}],"property2":{"$":"b1"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User.json
new file mode 100644
index 0000000..a614e56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User.json
@@ -0,0 +1 @@
+{"user":{"userid":{"$":"1621"},"name":{"$":"Grotefend"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable.json
new file mode 100644
index 0000000..78f213d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable.json
@@ -0,0 +1 @@
+{"userTable":{"columns":[{"id":{"$":"userid"},"label":{"$":"UserID"}},{"id":{"$":"name"},"label":{"$":"User Name"}}],"rows":{"userid":{"$":"1621"},"name":{"$":"Grotefend"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable_MOXy.json
new file mode 100644
index 0000000..78f213d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable_MOXy.json
@@ -0,0 +1 @@
+{"userTable":{"columns":[{"id":{"$":"userid"},"label":{"$":"UserID"}},{"id":{"$":"name"},"label":{"$":"User Name"}}],"rows":{"userid":{"$":"1621"},"name":{"$":"Grotefend"}}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User_MOXy.json
new file mode 100644
index 0000000..a614e56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User_MOXy.json
@@ -0,0 +1 @@
+{"user":{"userid":{"$":"1621"},"name":{"$":"Grotefend"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList.json
new file mode 100644
index 0000000..2c42549
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList.json
@@ -0,0 +1 @@
+{"animalList":{"animals":[{"@xsi.type":"dog","name":"Fifi"},{"@xsi.type":"cat","name":"Daisy"}]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList_MOXy.json
new file mode 100644
index 0000000..2c42549
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animalList":{"animals":[{"@xsi.type":"dog","name":"Fifi"},{"@xsi.type":"cat","name":"Daisy"}]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean.json
new file mode 100644
index 0000000..2d1c5d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean.json
@@ -0,0 +1 @@
+{"anotherArrayTestBean":{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean_MOXy.json
new file mode 100644
index 0000000..2d1c5d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean_MOXy.json
@@ -0,0 +1 @@
+{"anotherArrayTestBean":{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean.json
new file mode 100644
index 0000000..4b41cc9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean.json
@@ -0,0 +1 @@
+{"parent":{"@attr":"aval","$":"pval"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean_MOXy.json
new file mode 100644
index 0000000..4b41cc9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"parent":{"@attr":"aval","$":"pval"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes.json
new file mode 100644
index 0000000..4f8ff54
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes":{"@a2":"31415926","@a1":"hello dolly","filler1":111,"list":[{"@j":"bumper","@i":"312","@uri":"http://localhost:8080/jedna/bedna/","s1":"hi there"},{"@j":"bumper","@i":"312","@uri":"http://localhost:8080/jedna/bedna/","s1":"hi there"}],"filler2":222,"b":{"@j":"bumper","@i":"312","@uri":"http://localhost:8080/jedna/bedna/","s1":"hi there"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2.json
new file mode 100644
index 0000000..a738f36
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes2":{"@a2":"31415926","@a1":"hello dolly","filler1":111,"list":[{"@uri":"http://localhost:8080/jedna/bedna/"},{"@uri":"http://localhost:8080/jedna/bedna/"}],"filler2":222,"b":{"@uri":"http://localhost:8080/jedna/bedna/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
new file mode 100644
index 0000000..457c937
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes2":{"@a1":"hello dolly","@a2":"31415926","filler1":111,"list":[{"@uri":"http://localhost:8080/jedna/bedna/"},{"@uri":"http://localhost:8080/jedna/bedna/"}],"filler2":222,"b":{"@uri":"http://localhost:8080/jedna/bedna/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3.json
new file mode 100644
index 0000000..30c90d7
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes3":{"b":{"@uri":"http://localhost:8080/jedna/bedna/"},"c":{"@uri":"http://localhost:8080/jedna/bedna/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
new file mode 100644
index 0000000..30c90d7
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes3":{"b":{"@uri":"http://localhost:8080/jedna/bedna/"},"c":{"@uri":"http://localhost:8080/jedna/bedna/"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4.json
new file mode 100644
index 0000000..66e0fe6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes4":{"b":""}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
new file mode 100644
index 0000000..66e0fe6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes4":{"b":""}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..5b67d07
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"complexBeanWithAttributes":{"@a1":"hello dolly","@a2":"31415926","filler1":111,"list":[{"@uri":"http://localhost:8080/jedna/bedna/","@i":"312","@j":"bumper","s1":"hi there"},{"@uri":"http://localhost:8080/jedna/bedna/","@i":"312","@j":"bumper","s1":"hi there"}],"filler2":222,"b":{"@uri":"http://localhost:8080/jedna/bedna/","@i":"312","@j":"bumper","s1":"hi there"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean.json
new file mode 100644
index 0000000..f4c913e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean.json
@@ -0,0 +1 @@
+{"emptyElementBean":""}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean_MOXy.json
new file mode 100644
index 0000000..f4c913e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean_MOXy.json
@@ -0,0 +1 @@
+{"emptyElementBean":""}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean.json
new file mode 100644
index 0000000..1320e7f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean.json
@@ -0,0 +1 @@
+{"emptyElementContainingBean":{"c":"foo","d":"bar"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean_MOXy.json
new file mode 100644
index 0000000..1320e7f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean_MOXy.json
@@ -0,0 +1 @@
+{"emptyElementContainingBean":{"c":"foo","d":"bar"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean.json
new file mode 100644
index 0000000..e9119c3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean.json
@@ -0,0 +1 @@
+{"encodedContentBean":{"one":"\tone\n\tbig","two":"hafČ"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean_MOXy.json
new file mode 100644
index 0000000..e9119c3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean_MOXy.json
@@ -0,0 +1 @@
+{"encodedContentBean":{"one":"\tone\n\tbig","two":"hafČ"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean.json
new file mode 100644
index 0000000..657a01a
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean.json
@@ -0,0 +1 @@
+{"fakeArrayBean":{"weight":["1kg","2kg"],"color":"red","name":"bumper"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean_MOXy.json
new file mode 100644
index 0000000..657a01a
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean_MOXy.json
@@ -0,0 +1 @@
+{"fakeArrayBean":{"weight":["1kg","2kg"],"color":"red","name":"bumper"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray.json
new file mode 100644
index 0000000..ba9d1b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray.json
@@ -0,0 +1 @@
+{"intArray":{"intArray":4,"integerArray":3,"number":8}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray_MOXy.json
new file mode 100644
index 0000000..ba9d1b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray_MOXy.json
@@ -0,0 +1 @@
+{"intArray":{"intArray":4,"integerArray":3,"number":8}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List.json
new file mode 100644
index 0000000..2cf2ebf
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List.json
@@ -0,0 +1 @@
+{"jersey1199List":{"objects":[{"@xsi.type":"colorHolder","colors":["RED","BLUE"]},{"@xsi.type":"colorHolder","colors":"GREEN"}],"offset":0,"total":2}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List_MOXy.json
new file mode 100644
index 0000000..2cf2ebf
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List_MOXy.json
@@ -0,0 +1 @@
+{"jersey1199List":{"objects":[{"@xsi.type":"colorHolder","colors":["RED","BLUE"]},{"@xsi.type":"colorHolder","colors":"GREEN"}],"offset":0,"total":2}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean.json
new file mode 100644
index 0000000..d8db897
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean.json
@@ -0,0 +1 @@
+{"listAndNonListBean":{"a":1,"d":2}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean_MOXy.json
new file mode 100644
index 0000000..d8db897
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean_MOXy.json
@@ -0,0 +1 @@
+{"listAndNonListBean":{"a":1,"d":2}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean.json
new file mode 100644
index 0000000..5206895
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean.json
@@ -0,0 +1 @@
+{"listEmptyBean":""}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean_MOXy.json
new file mode 100644
index 0000000..5206895
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean_MOXy.json
@@ -0,0 +1 @@
+{"listEmptyBean":""}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean.json
new file mode 100644
index 0000000..ca068e9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean.json
@@ -0,0 +1 @@
+{"item":""}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean_MOXy.json
new file mode 100644
index 0000000..ca068e9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"item":""}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse.json
new file mode 100644
index 0000000..6b29b6e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse.json
@@ -0,0 +1 @@
+{"myResponse":{"jaxb.myMessage":[{"id":0,"text":"ok"},{"id":1,"text":"ok"}],"jaxb.myError":{"id":-1,"desc":"error"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse_MOXy.json
new file mode 100644
index 0000000..6b29b6e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse_MOXy.json
@@ -0,0 +1 @@
+{"myResponse":{"jaxb.myMessage":[{"id":0,"text":"ok"},{"id":1,"text":"ok"}],"jaxb.myError":{"id":-1,"desc":"error"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean.json
new file mode 100644
index 0000000..4f95fa4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean.json
@@ -0,0 +1 @@
+{"namespaceBean":{"a":"foo","example.b":"bar"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute.json
new file mode 100644
index 0000000..64e418a
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute.json
@@ -0,0 +1 @@
+{"namespaceBeanWithAttribute":{"@example.attr":"value","a":"foo","example.b":"bar"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
new file mode 100644
index 0000000..64e418a
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
@@ -0,0 +1 @@
+{"namespaceBeanWithAttribute":{"@example.attr":"value","a":"foo","example.b":"bar"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean_MOXy.json
new file mode 100644
index 0000000..4f95fa4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean_MOXy.json
@@ -0,0 +1 @@
+{"namespaceBean":{"a":"foo","example.b":"bar"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean.json
new file mode 100644
index 0000000..f3b4a4d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean.json
@@ -0,0 +1 @@
+{"nullStringBean":{"nullString":"not null to test if set to null works"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean_MOXy.json
new file mode 100644
index 0000000..f3b4a4d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean_MOXy.json
@@ -0,0 +1 @@
+{"nullStringBean":{"nullString":"not null to test if set to null works"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person.json
new file mode 100644
index 0000000..29a732e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person.json
@@ -0,0 +1 @@
+{"person":{"name":"Joe Schmo","children":{"child":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person_MOXy.json
new file mode 100644
index 0000000..29a732e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person_MOXy.json
@@ -0,0 +1 @@
+{"person":{"name":"Joe Schmo","children":{"child":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean.json
new file mode 100644
index 0000000..43044be
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean.json
@@ -0,0 +1 @@
+{"pureCharDataBean":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean_MOXy.json
new file mode 100644
index 0000000..43044be
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"pureCharDataBean":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage.json
new file mode 100644
index 0000000..308b42b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage.json
@@ -0,0 +1 @@
+{"registerMessage":{"@requestTime":"1234","@agentUID":"agentKocka"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage_MOXy.json
new file mode 100644
index 0000000..d288d49
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage_MOXy.json
@@ -0,0 +1 @@
+{"registerMessage":{"@agentUID":"agentKocka","@requestTime":"1234"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean.json
new file mode 100644
index 0000000..20eb30b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean.json
@@ -0,0 +1 @@
+{"DummyOne":{"child":"simple"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes.json
new file mode 100644
index 0000000..09bb1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes.json
@@ -0,0 +1 @@
+{"simpleBeanWithAttributes":{"@j":"bumper","@i":"312","@uri":"http://localhost:8080/jedna/bedna/","s1":"hi there"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..6be03ab
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"simpleBeanWithAttributes":{"@uri":"http://localhost:8080/jedna/bedna/","@i":"312","@j":"bumper","s1":"hi there"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute.json
new file mode 100644
index 0000000..ba42175
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttribute":{"@uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
new file mode 100644
index 0000000..8b18cf7
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttributeAndValue":{"@uri":"http://localhost:8080/jedna/bedna/","$":"characters"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
new file mode 100644
index 0000000..8b18cf7
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttributeAndValue":{"@uri":"http://localhost:8080/jedna/bedna/","$":"characters"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
new file mode 100644
index 0000000..ba42175
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
@@ -0,0 +1 @@
+{"simpleBeanWithJustOneAttribute":{"@uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean_MOXy.json
new file mode 100644
index 0000000..20eb30b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean_MOXy.json
@@ -0,0 +1 @@
+{"DummyOne":{"child":"simple"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean.json
new file mode 100644
index 0000000..1435abb
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean.json
@@ -0,0 +1 @@
+{"singleItemListWrapper":{"singleItemList":[1]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean_MOXy.json
new file mode 100644
index 0000000..1435abb
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"singleItemListWrapper":{"singleItemList":[1]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel.json
new file mode 100644
index 0000000..1b601d4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel.json
@@ -0,0 +1 @@
+{"treeModel":{"root":{"label":"dummy node","expanded":false}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel_MOXy.json
new file mode 100644
index 0000000..1b601d4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel_MOXy.json
@@ -0,0 +1 @@
+{"treeModel":{"root":{"label":"dummy node","expanded":false}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean.json
new file mode 100644
index 0000000..9a23326
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean.json
@@ -0,0 +1 @@
+{"item2":{"property1":["a1","a1"],"property2":"b1"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean_MOXy.json
new file mode 100644
index 0000000..9a23326
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"item2":{"property1":["a1","a1"],"property2":"b1"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User.json
new file mode 100644
index 0000000..a57ef80
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User.json
@@ -0,0 +1 @@
+{"user":{"userid":1621,"name":"Grotefend"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable.json
new file mode 100644
index 0000000..eed2f18
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable.json
@@ -0,0 +1 @@
+{"userTable":{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":{"userid":1621,"name":"Grotefend"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable_MOXy.json
new file mode 100644
index 0000000..eed2f18
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable_MOXy.json
@@ -0,0 +1 @@
+{"userTable":{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":{"userid":1621,"name":"Grotefend"}}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User_MOXy.json
new file mode 100644
index 0000000..a57ef80
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User_MOXy.json
@@ -0,0 +1 @@
+{"user":{"userid":1621,"name":"Grotefend"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnimalList.json
new file mode 100644
index 0000000..3da277e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"Dog","name":"Fifi"},{"@type":"Cat","nickName":null,"name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnimalList_MOXy.json
new file mode 100644
index 0000000..976ae9b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"className":"org.glassfish.jersey.tests.e2e.json.entity.Dog","instance":{"name":"Fifi"}},{"className":"org.glassfish.jersey.tests.e2e.json.entity.Cat","instance":{"name":"Daisy"}}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnotherArrayTestBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnotherArrayTestBean.json
new file mode 100644
index 0000000..d9dc02f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnotherArrayTestBean.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnotherArrayTestBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnotherArrayTestBean_MOXy.json
new file mode 100644
index 0000000..d9dc02f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AnotherArrayTestBean_MOXy.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AttrAndCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AttrAndCharDataBean.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AttrAndCharDataBean.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AttrAndCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AttrAndCharDataBean_MOXy.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_AttrAndCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes.json
new file mode 100644
index 0000000..0e88b3c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"b":{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"},"filler1":"111","filler2":"222","list":[{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"},{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes2.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes2.json
new file mode 100644
index 0000000..80d87b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes2.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"b":{"uri":"http://localhost:8080/jedna/bedna/"},"filler1":"111","filler2":"222","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes2_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes2_MOXy.json
new file mode 100644
index 0000000..80d87b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes2_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"b":{"uri":"http://localhost:8080/jedna/bedna/"},"filler1":"111","filler2":"222","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes3.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes3.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes3.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes3_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes3_MOXy.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes3_MOXy.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes4.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes4.json
new file mode 100644
index 0000000..cc0012f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes4.json
@@ -0,0 +1 @@
+{"b":{},"list":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes4_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes4_MOXy.json
new file mode 100644
index 0000000..cc0012f
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes4_MOXy.json
@@ -0,0 +1 @@
+{"b":{},"list":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..0e88b3c
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ComplexBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"b":{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"},"filler1":"111","filler2":"222","list":[{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"},{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"}]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementBean.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementBean.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementBean_MOXy.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementBean_MOXy.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementContainingBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementContainingBean.json
new file mode 100644
index 0000000..34819f9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementContainingBean.json
@@ -0,0 +1 @@
+{"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementContainingBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementContainingBean_MOXy.json
new file mode 100644
index 0000000..34819f9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EmptyElementContainingBean_MOXy.json
@@ -0,0 +1 @@
+{"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EncodedContentBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EncodedContentBean.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EncodedContentBean.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EncodedContentBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EncodedContentBean_MOXy.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_EncodedContentBean_MOXy.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_FakeArrayBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_FakeArrayBean.json
new file mode 100644
index 0000000..6c93316
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_FakeArrayBean.json
@@ -0,0 +1 @@
+{"color":"red","name":"bumper","weight":["1kg","2kg"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_FakeArrayBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_FakeArrayBean_MOXy.json
new file mode 100644
index 0000000..23c8825
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_FakeArrayBean_MOXy.json
@@ -0,0 +1 @@
+{"color":"red","name":"bumper","weight":["1kg","2kg"]}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_IntArray.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_IntArray.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_IntArray.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_IntArray_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_IntArray_MOXy.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_IntArray_MOXy.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Jersey1199List.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Jersey1199List.json
new file mode 100644
index 0000000..488cba2
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Jersey1199List.json
@@ -0,0 +1 @@
+{"objects":[{"colors":["RED","BLUE"]},{"colors":["GREEN"]}],"offset":0,"total":2}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Jersey1199List_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Jersey1199List_MOXy.json
new file mode 100644
index 0000000..488cba2
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Jersey1199List_MOXy.json
@@ -0,0 +1 @@
+{"objects":[{"colors":["RED","BLUE"]},{"colors":["GREEN"]}],"offset":0,"total":2}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListAndNonListBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListAndNonListBean.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListAndNonListBean.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListAndNonListBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListAndNonListBean_MOXy.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListAndNonListBean_MOXy.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListEmptyBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListEmptyBean.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListEmptyBean.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListEmptyBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListEmptyBean_MOXy.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListEmptyBean_MOXy.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListWrapperBean.json
new file mode 100644
index 0000000..379160e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListWrapperBean.json
@@ -0,0 +1 @@
+{"property":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListWrapperBean_MOXy.json
new file mode 100644
index 0000000..379160e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_ListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property":null}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_MyResponse.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_MyResponse.json
new file mode 100644
index 0000000..303d04e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_MyResponse.json
@@ -0,0 +1 @@
+{"myError":{"desc":"error","id":"-1"},"myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_MyResponse_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_MyResponse_MOXy.json
new file mode 100644
index 0000000..303d04e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_MyResponse_MOXy.json
@@ -0,0 +1 @@
+{"myError":{"desc":"error","id":"-1"},"myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBean.json
new file mode 100644
index 0000000..0386d5d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBean.json
@@ -0,0 +1 @@
+{"a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBeanWithAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBeanWithAttribute.json
new file mode 100644
index 0000000..ce17ce5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBeanWithAttribute.json
@@ -0,0 +1 @@
+{"a":"foo","attr":"value","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBeanWithAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBeanWithAttribute_MOXy.json
new file mode 100644
index 0000000..9915729
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBeanWithAttribute_MOXy.json
@@ -0,0 +1 @@
+{"a":"foo","attr":"value","b":"bar"}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBean_MOXy.json
new file mode 100644
index 0000000..0386d5d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NamespaceBean_MOXy.json
@@ -0,0 +1 @@
+{"a":"foo","b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NullStringBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NullStringBean.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NullStringBean.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NullStringBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NullStringBean_MOXy.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_NullStringBean_MOXy.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Person.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Person.json
new file mode 100644
index 0000000..84f9dcd
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Person.json
@@ -0,0 +1 @@
+{"children":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}],"name":"Joe Schmo"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Person_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Person_MOXy.json
new file mode 100644
index 0000000..7a660ed
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_Person_MOXy.json
@@ -0,0 +1 @@
+{"children":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}],"name":"Joe Schmo"}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PojoAnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PojoAnimalList.json
new file mode 100644
index 0000000..2a49520
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PojoAnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"PojoDog","name":"Fifi"},{"@type":"PojoCat","nickName":null,"name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PojoAnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PojoAnimalList_MOXy.json
new file mode 100644
index 0000000..2a49520
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PojoAnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"@type":"PojoDog","name":"Fifi"},{"@type":"PojoCat","nickName":null,"name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PureCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PureCharDataBean.json
new file mode 100644
index 0000000..9bdbc2b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PureCharDataBean.json
@@ -0,0 +1 @@
+{"content":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PureCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PureCharDataBean_MOXy.json
new file mode 100644
index 0000000..9bdbc2b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_PureCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"content":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_RegisterMessage.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_RegisterMessage.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_RegisterMessage.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_RegisterMessage_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_RegisterMessage_MOXy.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_RegisterMessage_MOXy.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBean.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBean.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithAttributes.json
new file mode 100644
index 0000000..498818a
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithAttributes.json
@@ -0,0 +1 @@
+{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..498818a
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"i":312,"j":"bumper","s1":"hi there","uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttribute.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttribute.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttributeAndValue.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBean_MOXy.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_SimpleBean_MOXy.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TreeModel.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TreeModel.json
new file mode 100644
index 0000000..f6d35c1
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TreeModel.json
@@ -0,0 +1 @@
+{"root":{"expanded":false,"label":"dummy node"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TreeModel_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TreeModel_MOXy.json
new file mode 100644
index 0000000..6b6746b
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TreeModel_MOXy.json
@@ -0,0 +1 @@
+{"root":{"expanded":false,"label":"dummy node"}}
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TwoListsWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TwoListsWrapperBean.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TwoListsWrapperBean.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TwoListsWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TwoListsWrapperBean_MOXy.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_TwoListsWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_User.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_User.json
new file mode 100644
index 0000000..7560ef0
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_User.json
@@ -0,0 +1 @@
+{"id":"1621","name":"Grotefend"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_UserTable.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_UserTable.json
new file mode 100644
index 0000000..8f15bbd
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_UserTable.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"id":"1621","name":"Grotefend"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_UserTable_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_UserTable_MOXy.json
new file mode 100644
index 0000000..8f15bbd
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_UserTable_MOXy.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"id":"1621","name":"Grotefend"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_User_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_User_MOXy.json
new file mode 100644
index 0000000..7560ef0
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JsonbTestProvider_User_MOXy.json
@@ -0,0 +1 @@
+{"id":"1621","name":"Grotefend"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList.json
new file mode 100644
index 0000000..51e361e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList.json
@@ -0,0 +1 @@
+{"animals":[{"xsi:type":"dog","name":"Fifi"},{"xsi:type":"cat","name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList_MOXy.json
new file mode 100644
index 0000000..51e361e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList_MOXy.json
@@ -0,0 +1 @@
+{"animals":[{"xsi:type":"dog","name":"Fifi"},{"xsi:type":"cat","name":"Daisy"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean.json
new file mode 100644
index 0000000..dfb3702
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean_MOXy.json
new file mode 100644
index 0000000..dfb3702
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean_MOXy.json
@@ -0,0 +1 @@
+{"cats":[{"name":"Foo","nickName":"Kitty"},{"name":"Bar","nickName":"Puss"}],"prop":"testProp"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean_MOXy.json
new file mode 100644
index 0000000..765e1f5
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"attr":"aval","value":"pval"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes.json
new file mode 100644
index 0000000..ae04acc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"},{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2.json
new file mode 100644
index 0000000..0a3dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
new file mode 100644
index 0000000..0a3dd56
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/"},{"uri":"http://localhost:8080/jedna/bedna/"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
new file mode 100644
index 0000000..7dd6379
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json
@@ -0,0 +1 @@
+{"b":{"uri":"http://localhost:8080/jedna/bedna/"},"c":{"uri":"http://localhost:8080/jedna/bedna/"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4.json
new file mode 100644
index 0000000..c724b09
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4.json
@@ -0,0 +1 @@
+{"list":[],"b":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
new file mode 100644
index 0000000..c724b09
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json
@@ -0,0 +1 @@
+{"list":[],"b":{}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..ae04acc
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"a1":"hello dolly","a2":31415926,"filler1":"111","list":[{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"},{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"}],"filler2":"222","b":{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean_MOXy.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean_MOXy.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean.json
new file mode 100644
index 0000000..34819f9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean.json
@@ -0,0 +1 @@
+{"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean_MOXy.json
new file mode 100644
index 0000000..34819f9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean_MOXy.json
@@ -0,0 +1 @@
+{"c":"foo","d":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean_MOXy.json
new file mode 100644
index 0000000..b4de3a6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean_MOXy.json
@@ -0,0 +1 @@
+{"one":"\tone\n\tbig","two":"hafČ"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean.json
new file mode 100644
index 0000000..f32b098
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean.json
@@ -0,0 +1 @@
+{"weight":["1kg","2kg"],"color":"red","name":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean_MOXy.json
new file mode 100644
index 0000000..f32b098
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean_MOXy.json
@@ -0,0 +1 @@
+{"weight":["1kg","2kg"],"color":"red","name":"bumper"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray_MOXy.json
new file mode 100644
index 0000000..4445714
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray_MOXy.json
@@ -0,0 +1 @@
+{"intArray":[4],"integerArray":[3],"number":8}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List.json
new file mode 100644
index 0000000..7481af3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List.json
@@ -0,0 +1 @@
+{"objects":[{"xsi:type":"colorHolder","colors":["RED","BLUE"]},{"xsi:type":"colorHolder","colors":["GREEN"]}],"offset":0,"total":2}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List_MOXy.json
new file mode 100644
index 0000000..7481af3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List_MOXy.json
@@ -0,0 +1 @@
+{"objects":[{"xsi:type":"colorHolder","colors":["RED","BLUE"]},{"xsi:type":"colorHolder","colors":["GREEN"]}],"offset":0,"total":2}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean_MOXy.json
new file mode 100644
index 0000000..f85cad9
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean_MOXy.json
@@ -0,0 +1 @@
+{"a":["1"],"d":"2"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean_MOXy.json
new file mode 100644
index 0000000..b5a9f53
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean_MOXy.json
@@ -0,0 +1 @@
+{"empty":[]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean_MOXy.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse.json
new file mode 100644
index 0000000..b054d67
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse.json
@@ -0,0 +1 @@
+{"jaxb:myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}],"jaxb:myError":{"id":"-1","desc":"error"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse_MOXy.json
new file mode 100644
index 0000000..b054d67
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse_MOXy.json
@@ -0,0 +1 @@
+{"jaxb:myMessage":[{"id":"0","text":"ok"},{"id":"1","text":"ok"}],"jaxb:myError":{"id":"-1","desc":"error"}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean.json
new file mode 100644
index 0000000..c8df655
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean.json
@@ -0,0 +1 @@
+{"a":"foo","example:b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute.json
new file mode 100644
index 0000000..6158c88
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute.json
@@ -0,0 +1 @@
+{"example:attr":"value","a":"foo","example:b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
new file mode 100644
index 0000000..6158c88
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json
@@ -0,0 +1 @@
+{"example:attr":"value","a":"foo","example:b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean_MOXy.json
new file mode 100644
index 0000000..c8df655
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean_MOXy.json
@@ -0,0 +1 @@
+{"a":"foo","example:b":"bar"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean_MOXy.json
new file mode 100644
index 0000000..4a8cd3d
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean_MOXy.json
@@ -0,0 +1 @@
+{"nullString":"not null to test if set to null works"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person.json
new file mode 100644
index 0000000..8ccd39e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person.json
@@ -0,0 +1 @@
+{"name":"Joe Schmo","children":{"child":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person_MOXy.json
new file mode 100644
index 0000000..8ccd39e
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person_MOXy.json
@@ -0,0 +1 @@
+{"name":"Joe Schmo","children":{"child":[{"name":"Jill Schmo"},{"name":"Jack Schmo"}]}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean.json
new file mode 100644
index 0000000..13d3153
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean.json
@@ -0,0 +1 @@
+{"value":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean_MOXy.json
new file mode 100644
index 0000000..13d3153
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean_MOXy.json
@@ -0,0 +1 @@
+{"value":"some textual content"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage_MOXy.json
new file mode 100644
index 0000000..49f49b3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage_MOXy.json
@@ -0,0 +1 @@
+{"agentUID":"agentKocka","requestTime":1234}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes.json
new file mode 100644
index 0000000..bed08d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
new file mode 100644
index 0000000..bed08d6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","i":312,"j":"bumper","s1":"hi there"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
new file mode 100644
index 0000000..8beca20
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/","value":"characters"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
new file mode 100644
index 0000000..a989896
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json
@@ -0,0 +1 @@
+{"uri":"http://localhost:8080/jedna/bedna/"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean_MOXy.json
new file mode 100644
index 0000000..0f380b4
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean_MOXy.json
@@ -0,0 +1 @@
+{"child":"simple"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel.json
new file mode 100644
index 0000000..7ffadb3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel.json
@@ -0,0 +1 @@
+{"root":{"label":"dummy node","expanded":false}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel_MOXy.json
new file mode 100644
index 0000000..7ffadb3
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel_MOXy.json
@@ -0,0 +1 @@
+{"root":{"label":"dummy node","expanded":false}}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean_MOXy.json
new file mode 100644
index 0000000..d4ff239
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean_MOXy.json
@@ -0,0 +1 @@
+{"property1":["a1","a1"],"property2":["b1"]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User.json
new file mode 100644
index 0000000..a1b91b6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User.json
@@ -0,0 +1 @@
+{"userid":"1621","name":"Grotefend"}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable.json
new file mode 100644
index 0000000..2652195
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"userid":"1621","name":"Grotefend"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable_MOXy.json
new file mode 100644
index 0000000..2652195
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable_MOXy.json
@@ -0,0 +1 @@
+{"columns":[{"id":"userid","label":"UserID"},{"id":"name","label":"User Name"}],"rows":[{"userid":"1621","name":"Grotefend"}]}
\ No newline at end of file
diff --git a/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User_MOXy.json b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User_MOXy.json
new file mode 100644
index 0000000..a1b91b6
--- /dev/null
+++ b/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User_MOXy.json
@@ -0,0 +1 @@
+{"userid":"1621","name":"Grotefend"}
\ No newline at end of file
diff --git a/tests/e2e-inject/cdi2-se/pom.xml b/tests/e2e-inject/cdi2-se/pom.xml
new file mode 100644
index 0000000..86c9156
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>e2e-inject</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-inject-cdi2-se</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e-inject-cdi-se</name>
+
+    <description>Jersey E2E Inject CDI SE tests</description>
+
+    <properties>
+        <cdi.api.version>2.0</cdi.api.version>
+        <weld.version>${weld3.version}</weld.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-cdi2-se</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>xdk</id>
+            <properties>
+                <!-- do not use security manager for xdk -->
+                <surefire.security.argline />
+            </properties>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Account.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Account.java
new file mode 100644
index 0000000..7651449
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Account.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+
+/**
+ * Keeps current state of money.
+ *
+ * @author Petr Bouda
+ */
+@ApplicationScoped
+public class Account {
+
+    private long current = 0;
+
+    public void observeCredit(@Observes @Credit Long amount) {
+        current += amount;
+    }
+
+    public void observeDebit(@Observes @Debit Long amount) {
+        current -= amount;
+    }
+
+    public long getCurrent() {
+        return current;
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/AccountResource.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/AccountResource.java
new file mode 100644
index 0000000..5073fad
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/AccountResource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+/**
+ * Testing resource for CDI events.
+ *
+ * @author Petr Bouda
+ */
+@Path("account")
+public class AccountResource {
+
+    @Inject
+    @Credit
+    private Event<Long> creditEvent;
+
+    @Inject
+    @Debit
+    private Event<Long> debitEvent;
+
+    @Inject
+    private Account account;
+
+    @POST
+    public void credit(@QueryParam("amount") long amount) {
+        creditEvent.fire(amount);
+    }
+
+    @DELETE
+    public void debit(@QueryParam("amount") long amount) {
+        debitEvent.fire(amount);
+    }
+
+    @GET
+    public long current() {
+        return account.getCurrent();
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Credit.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Credit.java
new file mode 100644
index 0000000..be693da
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Credit.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Event qualifier.
+ *
+ * @author Petr Bouda
+ */
+@Qualifier
+@Target({METHOD, FIELD, PARAMETER, TYPE})
+@Retention(RUNTIME)
+public @interface Credit {
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Debit.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Debit.java
new file mode 100644
index 0000000..adef1fc
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Debit.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.inject.Qualifier;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Event qualifier.
+ *
+ * @author Petr Bouda
+ */
+@Qualifier
+@Target({METHOD, FIELD, PARAMETER, TYPE})
+@Retention(RUNTIME)
+public @interface Debit {
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Hello.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Hello.java
new file mode 100644
index 0000000..da76a10
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Hello.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+/**
+ * Interface used to activate decorator.
+ *
+ * @author Petr Bouda
+ */
+public interface Hello {
+
+    String hello();
+
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/HelloResource.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/HelloResource.java
new file mode 100644
index 0000000..9780494
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/HelloResource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import javax.inject.Inject;
+
+/**
+ * Intercepted and decorated resource.
+ *
+ * @author Petr Bouda
+ */
+@Secured
+@Path("intercepted")
+public class HelloResource implements Hello {
+
+    @Inject
+    private NameService service;
+
+    @GET
+    @Override
+    public String hello() {
+        return "Hello " + service.getName();
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/HelloStarDecorator.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/HelloStarDecorator.java
new file mode 100644
index 0000000..cba9ebf
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/HelloStarDecorator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.decorator.Decorator;
+import javax.decorator.Delegate;
+import javax.enterprise.inject.Any;
+import javax.inject.Inject;
+
+/**
+ * Decorator wraps the hello resource by stars.
+ *
+ * @author Petr Bouda
+ */
+@Decorator
+public class HelloStarDecorator implements Hello {
+
+    @Inject
+    @Delegate
+    @Any
+    Hello account;
+
+    @Override
+    public String hello() {
+        return "***" + account.hello() + "***";
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/JaxrsService.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/JaxrsService.java
new file mode 100644
index 0000000..5a708be
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/JaxrsService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Holder for JAX-RS information to inject them into interceptor. JAX-RS does not work in interceptor.
+ *
+ * @author Petr Bouda
+ */
+@ApplicationScoped
+public class JaxrsService {
+
+    @Context
+    private UriInfo uriInfo;
+
+    public UriInfo getUriInfo() {
+        return uriInfo;
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/NameService.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/NameService.java
new file mode 100644
index 0000000..6aa4555
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/NameService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Service returning the name.
+ *
+ * @author Petr Bouda
+ */
+@ApplicationScoped
+public class NameService {
+
+    public static final String NAME = "James";
+
+    public String getName() {
+       return NAME;
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Secured.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Secured.java
new file mode 100644
index 0000000..762ca0e
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/Secured.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.interceptor.InterceptorBinding;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Security annotation bound to {@link SecurityInterceptor}.
+ *
+ * @author Petr Bouda
+ */
+@Inherited
+@InterceptorBinding
+@Retention(RUNTIME)
+@Target({ METHOD, TYPE })
+public @interface Secured {
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/SecurityInterceptor.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/SecurityInterceptor.java
new file mode 100644
index 0000000..5beae95
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/SecurityInterceptor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.core.MultivaluedMap;
+
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+/**
+ * Interceptor checking James as a user in query params.
+ *
+ * @author Petr Bouda
+ */
+@Secured
+@Interceptor
+public class SecurityInterceptor {
+
+    @Inject
+    NameService nameService;
+
+    @Inject
+    JaxrsService jaxrsService;
+
+    @AroundInvoke
+    public Object logMethodEntry(InvocationContext ctx) throws Exception {
+        MultivaluedMap<String, String> params = jaxrsService.getUriInfo().getQueryParameters();
+        String user = params.getFirst("user");
+
+        if (nameService.getName().equals(user)) {
+            return ctx.proceed();
+        } else {
+            throw new ForbiddenException("Forbidden resource for the user: " + user);
+        }
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/ApplicationCounterBean.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/ApplicationCounterBean.java
new file mode 100644
index 0000000..2830975
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/ApplicationCounterBean.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.scopes;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Request counter.
+ *
+ * @author Petr Bouda
+ */
+@ApplicationScoped
+public class ApplicationCounterBean {
+
+    private AtomicInteger counter = new AtomicInteger();
+
+    public int getNumber() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/RequestScopedResource.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/RequestScopedResource.java
new file mode 100644
index 0000000..2d77691
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/RequestScopedResource.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.scopes;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.process.internal.RequestScoped;
+
+/**
+ * Request scoped resource.
+ *
+ * @author Petr Bouda
+ */
+@RequestScoped
+@Path("request")
+public class RequestScopedResource {
+
+    @Inject
+    private ApplicationCounterBean application;
+
+    @PathParam("name")
+    private String name;
+
+    private UriInfo uriInfo;
+
+    public RequestScopedResource(@Context UriInfo uriInfo) {
+        this.uriInfo = uriInfo;
+    }
+
+    @GET
+    @Path("{name}")
+    @Produces("text/plain")
+    public String getHello() {
+        return "Hello_" + name + " [" + application.getNumber() + "] " + "[" + uriInfo.getPath() + "] " + this;
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/SingletonScopedResource.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/SingletonScopedResource.java
new file mode 100644
index 0000000..6551a1e
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/SingletonScopedResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.scopes;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Singleton Resource.
+ *
+ * @author Petr Bouda
+ */
+@Singleton
+@Path("singleton")
+public class SingletonScopedResource {
+
+    @Inject
+    private ApplicationCounterBean application;
+
+    @Context
+    private UriInfo uriInfo;
+
+    @GET
+    @Path("{name}")
+    @Produces("text/plain")
+    public String getHello(@PathParam("name") String name) {
+        return "Hello_" + name + " [" + application.getNumber() + "] " + "[" + uriInfo.getPath() + "] " + this;
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/ModelProcessorFeature.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/ModelProcessorFeature.java
new file mode 100644
index 0000000..b4780ad
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/ModelProcessorFeature.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.subresources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+
+import javax.annotation.Priority;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.model.ModelProcessor;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.model.ResourceModel;
+
+public class ModelProcessorFeature implements Feature {
+
+    @Override
+    public boolean configure(FeatureContext context) {
+        context.register(SimpleModelProcessor.class);
+        return true;
+    }
+
+    @Priority(5000)
+    public static class SimpleModelProcessor implements ModelProcessor {
+
+        @Override
+        public ResourceModel processResourceModel(ResourceModel resourceModel, Configuration configuration) {
+            ResourceModel.Builder builder = new ResourceModel.Builder(resourceModel.getRootResources(), false);
+            final Resource singletonResource = Resource.from(SingletonResource.class);
+            builder.addResource(singletonResource);
+
+            final Resource requestScopeResource = Resource.from(RequestScopeResource.class);
+            builder.addResource(requestScopeResource);
+
+            final Resource.Builder resourceBuilder = Resource.builder("instance");
+            resourceBuilder.addMethod("GET").handledBy(new Inflector<ContainerRequestContext, String>() {
+                private int counter = 0;
+
+                @Override
+                public String apply(ContainerRequestContext containerRequestContext) {
+                    return String.valueOf("Inflector:" + counter++);
+                }
+            });
+            final Resource instanceResource = resourceBuilder.build();
+
+            builder.addResource(instanceResource);
+
+            return builder.build();
+        }
+
+        @Override
+        public ResourceModel processSubResource(ResourceModel subResource, Configuration configuration) {
+            final Resource resource = Resource.builder()
+                    .mergeWith(Resource.from(EnhancedSubResourceSingleton.class))
+                    .mergeWith(Resource.from(EnhancedSubResource.class))
+                    .mergeWith(subResource.getResources().get(0)).build();
+
+            return new ResourceModel.Builder(true).addResource(resource).build();
+        }
+    }
+
+    @Singleton
+    public static class EnhancedSubResourceSingleton {
+        private int counter = 0;
+
+        @GET
+        @Path("enhanced-singleton")
+        public String get() {
+            return "EnhancedSubResourceSingleton:" + String.valueOf(counter++);
+        }
+    }
+
+    public static class EnhancedSubResource {
+
+        private int counter = 0;
+
+        @GET
+        @Path("enhanced")
+        public String get() {
+            return String.valueOf("EnhancedSubResource:" + counter++);
+        }
+    }
+
+    @Path("request-scope")
+    public static class RequestScopeResource {
+        private int counter = 0;
+
+        @GET
+        public String get() {
+            return String.valueOf("RequestScopeResource:" + counter++);
+        }
+    }
+
+    @Path("singleton")
+    @Singleton
+    public static class SingletonResource {
+        private int counter = 0;
+
+        @GET
+        public String get() {
+            return String.valueOf("SingletonResource:" + counter++);
+        }
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/RootResource.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/RootResource.java
new file mode 100644
index 0000000..3ea11db
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/RootResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.subresources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+@Path("root")
+public class RootResource {
+
+    @GET
+    public String get() {
+        return "root";
+    }
+
+    @Path("sub-resource-singleton")
+    public Class<SubResourceSingleton> getSubResourceSingleton() {
+        return SubResourceSingleton.class;
+    }
+
+    @Path("sub-resource-instance")
+    public SubResourceSingleton getSubResourceSingletonInstance() {
+        return new SubResourceSingleton();
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/RootSingletonResource.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/RootSingletonResource.java
new file mode 100644
index 0000000..fa178d7
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/RootSingletonResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.subresources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import javax.inject.Singleton;
+
+@Path("root-singleton")
+@Singleton
+public class RootSingletonResource {
+
+    private int counter = 0;
+
+    @GET
+    public String get() {
+        return "RootSingletonResource:" + String.valueOf(counter++);
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/SubResourceSingleton.java b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/SubResourceSingleton.java
new file mode 100644
index 0000000..c1dbd3b
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/SubResourceSingleton.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.subresources;
+
+import javax.ws.rs.GET;
+
+import javax.inject.Singleton;
+
+@Singleton
+public class SubResourceSingleton {
+
+    private int counter = 0;
+
+    @GET
+    public String get() {
+        return String.valueOf("SubResourceSingleton:" + counter++);
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml b/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..ae84224
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<beans>
+    <interceptors>
+        <class>org.glassfish.jersey.tests.e2e.inject.cdi.se.SecurityInterceptor</class>
+    </interceptors>
+    <decorators>
+        <class>org.glassfish.jersey.tests.e2e.inject.cdi.se.HelloStarDecorator</class>
+    </decorators>
+</beans>
diff --git a/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/EventsTest.java b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/EventsTest.java
new file mode 100644
index 0000000..e642750
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/EventsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that the resource can fire an event.
+ *
+ * @author Petr Bouda
+ */
+public class EventsTest extends JerseyTest{
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(AccountResource.class);
+    }
+
+    @Test
+    public void testFiredEvents() {
+        Response credit = target("account").queryParam("amount", 50).request().post(Entity.json(""));
+        assertEquals(204, credit.getStatus());
+
+        Response debit = target("account").queryParam("amount", 25).request().delete();
+        assertEquals(204, debit.getStatus());
+
+        Long current = target("account").queryParam("amount", 25).request().get(Long.class);
+        assertEquals(25, current.longValue());
+    }
+
+}
diff --git a/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/InterceptorDecoratorTest.java b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/InterceptorDecoratorTest.java
new file mode 100644
index 0000000..b3f70a3
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/InterceptorDecoratorTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that the resource can be intercepted and decorated.
+ *
+ * @author Petr Bouda
+ */
+public class InterceptorDecoratorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HelloResource.class);
+    }
+
+    @Test
+    public void testInterceptedGet() {
+        String intercepted = target("intercepted").queryParam("user", NameService.NAME).request().get(String.class);
+        assertEquals("***Hello James***", intercepted);
+    }
+
+    @Test
+    public void testForbiddenGet() {
+        Response result = target("intercepted").queryParam("user", "unknown").request().get();
+        assertEquals(403, result.getStatus());
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/RequestContextBuilder.java b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/RequestContextBuilder.java
new file mode 100644
index 0000000..3b06a47
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/RequestContextBuilder.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.ext.RuntimeDelegate;
+import javax.ws.rs.ext.WriterInterceptor;
+
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.PropertiesDelegate;
+import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.message.internal.HeaderUtils;
+import org.glassfish.jersey.server.ContainerRequest;
+
+/**
+ * Used by tests to create mock JerseyContainerRequestContext instances.
+ *
+ * @author Martin Matula
+ */
+public class RequestContextBuilder {
+
+    public class TestContainerRequest extends ContainerRequest {
+
+        private Object entity;
+        private GenericType entityType;
+        private final PropertiesDelegate propertiesDelegate;
+
+        public TestContainerRequest(final URI baseUri,
+                                    final URI requestUri,
+                                    final String method,
+                                    final SecurityContext securityContext,
+                                    final PropertiesDelegate propertiesDelegate) {
+            super(baseUri, requestUri, method, securityContext, propertiesDelegate);
+            this.propertiesDelegate = propertiesDelegate;
+        }
+
+        public void setEntity(final Object entity) {
+            if (entity instanceof GenericEntity) {
+                this.entity = ((GenericEntity) entity).getEntity();
+                this.entityType = new GenericType(((GenericEntity) entity).getType());
+            } else {
+                this.entity = entity;
+                this.entityType = new GenericType(entity.getClass());
+            }
+        }
+
+        @Override
+        public void setWorkers(final MessageBodyWorkers workers) {
+            super.setWorkers(workers);
+            final byte[] entityBytes;
+            if (entity != null) {
+                final MultivaluedMap<String, Object> myMap = new MultivaluedHashMap<String, Object>(getHeaders());
+                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                OutputStream stream = null;
+                try {
+                    stream = workers.writeTo(entity, entity.getClass(), entityType.getType(),
+                            new Annotation[0], getMediaType(),
+                            myMap,
+                            propertiesDelegate, baos, Collections.<WriterInterceptor>emptyList());
+                } catch (final IOException | WebApplicationException ex) {
+                    Logger.getLogger(TestContainerRequest.class.getName()).log(Level.SEVERE, null, ex);
+                } finally {
+                    if (stream != null) {
+                        try {
+                            stream.close();
+                        } catch (final IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+                entityBytes = baos.toByteArray();
+            } else {
+                entityBytes = new byte[0];
+            }
+            setEntityStream(new ByteArrayInputStream(entityBytes));
+        }
+    }
+
+    private final RuntimeDelegate delegate = RuntimeDelegate.getInstance();
+    private final TestContainerRequest request;
+
+    public static RequestContextBuilder from(final String requestUri, final String method) {
+        return from(null, requestUri, method);
+    }
+
+    public static RequestContextBuilder from(final String baseUri, final String requestUri, final String method) {
+        return new RequestContextBuilder(baseUri, requestUri, method);
+    }
+
+    public static RequestContextBuilder from(final URI requestUri, final String method) {
+        return from(null, requestUri, method);
+    }
+
+    public static RequestContextBuilder from(final URI baseUri, final URI requestUri, final String method) {
+        return new RequestContextBuilder(baseUri, requestUri, method);
+    }
+
+    private RequestContextBuilder(final String baseUri, final String requestUri, final String method) {
+        this(baseUri == null || baseUri.isEmpty() ? null : URI.create(baseUri), URI.create(requestUri), method);
+    }
+
+    private RequestContextBuilder(final URI baseUri, final URI requestUri, final String method) {
+        request = new TestContainerRequest(baseUri, requestUri, method, null,
+                new MapPropertiesDelegate());
+    }
+
+    public ContainerRequest build() {
+        return request;
+    }
+
+    public RequestContextBuilder accept(final String... acceptHeader) {
+        putHeaders(HttpHeaders.ACCEPT, acceptHeader);
+        return this;
+    }
+
+    public RequestContextBuilder accept(final MediaType... acceptHeader) {
+        putHeaders(HttpHeaders.ACCEPT, (Object[]) acceptHeader);
+        return this;
+    }
+
+    public RequestContextBuilder entity(final Object entity) {
+        request.setEntity(entity);
+        return this;
+    }
+
+    public RequestContextBuilder type(final String contentType) {
+        request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, contentType);
+        return this;
+
+    }
+
+    public RequestContextBuilder type(final MediaType contentType) {
+        request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HeaderUtils.asString(contentType, delegate));
+        return this;
+    }
+
+    public RequestContextBuilder header(final String name, final Object value) {
+        putHeader(name, value);
+        return this;
+    }
+
+    public RequestContextBuilder cookie(final Cookie cookie) {
+        putHeader(HttpHeaders.COOKIE, cookie);
+        return this;
+    }
+
+    public RequestContextBuilder cookies(final Cookie... cookies) {
+        putHeaders(HttpHeaders.COOKIE, (Object[]) cookies);
+        return this;
+    }
+
+    private void putHeader(final String name, final Object value) {
+        if (value == null) {
+            request.getHeaders().remove(name);
+            return;
+        }
+        request.header(name, HeaderUtils.asString(value, delegate));
+    }
+
+    private void putHeaders(final String name, final Object... values) {
+        if (values == null) {
+            request.getHeaders().remove(name);
+            return;
+        }
+        request.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), delegate));
+    }
+
+    private void putHeaders(final String name, final String... values) {
+        if (values == null) {
+            request.getHeaders().remove(name);
+            return;
+        }
+        request.getHeaders().addAll(name, values);
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/ScopesTest.java b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/ScopesTest.java
new file mode 100644
index 0000000..96be176
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/scopes/ScopesTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.scopes;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+/**
+ * Tests CDI resources.
+ */
+public class ScopesTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(RequestScopedResource.class, SingletonScopedResource.class);
+    }
+
+    @Test
+    public void testCheckRequest() throws InterruptedException {
+        String[] response1 = target().path("request").path("James").request().get(String.class).split(" ");
+        String[] response2 = target().path("request").path("Marcus").request().get(String.class).split(" ");
+        assertResponses("request", response1, response2);
+        assertNotEquals(response1[3], response2[3]);
+    }
+
+    @Test
+    public void testCheckSingleton() throws InterruptedException {
+        String[] response1 = target().path("singleton").path("James").request().get(String.class).split(" ");
+        String[] response2 = target().path("singleton").path("Marcus").request().get(String.class).split(" ");
+        assertResponses("singleton", response1, response2);
+        assertEquals(response1[3], response2[3]);
+    }
+
+    private void assertResponses(String type, String[] response1, String[] response2) {
+        assertEquals("Hello_James", response1[0]);
+        assertEquals("[1]", response1[1]);
+        assertEquals("[" + type + "/James]", response1[2]);
+
+        assertEquals("Hello_Marcus", response2[0]);
+        assertEquals("[2]", response2[1]);
+        assertEquals("[" + type + "/Marcus]", response2[2]);
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/ModelProcessorScopeTest.java b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/ModelProcessorScopeTest.java
new file mode 100644
index 0000000..c431a65
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/se/subresources/ModelProcessorScopeTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2017, 2018 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.inject.cdi.se.subresources;
+
+import java.util.concurrent.ExecutionException;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.e2e.inject.cdi.se.RequestContextBuilder;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test scope of resources enhanced by model processors.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class ModelProcessorScopeTest {
+
+    private void _testCounter(ApplicationHandler applicationHandler, String requestUri, final String prefix,
+                              final String expectedSecondHit) throws
+            InterruptedException, ExecutionException {
+        ContainerResponse response = applicationHandler.apply(RequestContextBuilder.from(requestUri,
+                "GET").build()).get();
+        assertEquals(200, response.getStatus());
+        assertEquals(prefix + ":0", response.getEntity());
+        response = applicationHandler.apply(RequestContextBuilder.from(requestUri,
+                "GET").build()).get();
+        assertEquals(prefix + ":" + expectedSecondHit, response.getEntity());
+    }
+
+    @Test
+    public void testSingleton() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(ModelProcessorFeature
+                .SingletonResource.class));
+        final String requestUri = "/singleton";
+        _testCounter(applicationHandler, requestUri, "SingletonResource", "1");
+    }
+
+    @Test
+    public void testSingletonInModelProcessor() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class,
+                ModelProcessorFeature.class));
+        final String requestUri = "/singleton";
+        _testCounter(applicationHandler, requestUri, "SingletonResource", "1");
+    }
+
+    @Test
+    public void testSubResourceSingletonInOriginalModel() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class));
+        final String requestUri = "/root/sub-resource-singleton";
+        _testCounter(applicationHandler, requestUri, "SubResourceSingleton", "1");
+    }
+
+    @Test
+    public void testSubResourceEnhancedSingleton() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class));
+        final String requestUri = "/root/sub-resource-singleton/enhanced-singleton";
+        _testCounter(applicationHandler, requestUri, "EnhancedSubResourceSingleton", "1");
+    }
+
+    @Test
+    public void testSubResourceInstanceEnhancedSingleton() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class));
+        final String requestUri = "/root/sub-resource-instance/enhanced-singleton";
+        _testCounter(applicationHandler, requestUri, "EnhancedSubResourceSingleton", "1");
+    }
+
+    @Test
+    public void testSubResourceInstanceEnhancedSubResource() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class));
+        final String requestUri = "/root/sub-resource-instance/enhanced";
+        _testCounter(applicationHandler, requestUri, "EnhancedSubResource", "0");
+    }
+
+    @Test
+    public void testSubResourceEnhancedSubResource() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class));
+        final String requestUri = "/root/sub-resource-singleton/enhanced";
+        _testCounter(applicationHandler, requestUri, "EnhancedSubResource", "0");
+    }
+
+    @Test
+    public void testInstanceInModelProcessor() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class,
+                ModelProcessorFeature.class));
+        final String requestUri = "/instance";
+        _testCounter(applicationHandler, requestUri, "Inflector", "1");
+    }
+
+    @Test
+    public void testRootSingleton() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class,
+                RootSingletonResource.class));
+        final String requestUri = "/root-singleton";
+        _testCounter(applicationHandler, requestUri, "RootSingletonResource", "1");
+    }
+
+    @Test
+    public void testRequestScopeResource() throws ExecutionException, InterruptedException {
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class,
+                RootSingletonResource.class, ModelProcessorFeature.class));
+        final String requestUri = "/request-scope";
+        _testCounter(applicationHandler, requestUri, "RequestScopeResource", "0");
+    }
+}
diff --git a/tests/e2e-inject/cdi2-se/src/test/resources/surefire.policy b/tests/e2e-inject/cdi2-se/src/test/resources/surefire.policy
new file mode 100644
index 0000000..4e99a5d
--- /dev/null
+++ b/tests/e2e-inject/cdi2-se/src/test/resources/surefire.policy
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017, 2018 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
+ */
+
+// we do not care about java lib itself
+grant codebase "file:${java.home}/-" {
+  permission java.security.AllPermission;
+};
+
+// we do not care about our dependencies
+grant codebase "file:${settings.localRepository}/-" {
+  permission java.security.AllPermission;
+};
+
+// this is to be able to set runtime delegate instance in jax-rs from the tests
+// and to run multi-threaded tests
+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.lang.RuntimePermission "getClassLoader";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
+  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+};
+
+grant codebase "file:${project.build.directory}/classes/-" {
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+  permission java.lang.RuntimePermission "accessDeclaredMembers";
+  permission java.lang.RuntimePermission "getClassLoader";
+  permission java.lang.RuntimePermission "modifyThread";
+  permission java.util.PropertyPermission "*", "read";
+  permission java.io.FilePermission "<<ALL FILES>>", "read";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
+  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+};
diff --git a/tests/e2e-inject/pom.xml b/tests/e2e-inject/pom.xml
new file mode 100644
index 0000000..97ed3f8
--- /dev/null
+++ b/tests/e2e-inject/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-inject</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-e2e-inject</name>
+
+    <description>Jersey E2E Inject tests</description>
+
+    <modules>
+        <module>cdi2-se</module>
+    </modules>
+</project>
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
new file mode 100644
index 0000000..6e7ecd9
--- /dev/null
+++ b/tests/e2e-server/pom.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-server</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e-server</name>
+
+    <description>Jersey E2E Server tests</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-freemarker</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-mustache</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-signature</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth2-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>xmlunit</groupId>
+            <artifactId>xmlunit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>xdk</id>
+            <properties>
+                <!-- do not use security manager for xdk -->
+                <surefire.security.argline />
+            </properties>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+    </profiles>
+
+</project>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AbstractDisableMetainfServicesLookupTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AbstractDisableMetainfServicesLookupTest.java
new file mode 100644
index 0000000..3520675
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AbstractDisableMetainfServicesLookupTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.ServiceFinderBinder;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+
+/**
+ * This is base for tests testing enabling/disabling configuration property
+ * {@link org.glassfish.jersey.server.ServerProperties#METAINF_SERVICES_LOOKUP_DISABLE}.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public abstract class AbstractDisableMetainfServicesLookupTest extends JerseyTest {
+
+    protected void testGet(int expectedGetResponseCode, int expectedPostResponseCode) throws Exception {
+        final String name = "Jersey";
+        {
+            Response response = target("/").path(name).request().get();
+            Assert.assertEquals(expectedGetResponseCode, response.getStatus());
+
+            if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
+                UselessMessage entity = response.readEntity(UselessMessage.class);
+                if (entity != null) {
+                    Assert.assertEquals("Hello " + name, entity.getMessage());
+                }
+            }
+        }
+        {
+            Entity<UselessMessage> uselessMessageEntity = Entity.entity(new UselessMessage(name), MediaType.TEXT_PLAIN_TYPE);
+            Response response = target("/").request().post(uselessMessageEntity);
+            Assert.assertEquals(expectedPostResponseCode, response.getStatus());
+
+            if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
+                String entity = response.readEntity(String.class);
+                if (entity.length() > 0) {
+                    Assert.assertEquals(name, entity);
+                }
+            }
+        }
+    }
+
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig config = new ResourceConfig(Resource.class);
+        config.register(new MetainfServicesBinder(config));
+        return config;
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(new MetainfServicesBinder(config));
+    }
+
+    @Path("/")
+    @Produces("text/plain")
+    @Consumes("text/plain")
+    public static class Resource {
+
+        @GET
+        @Path("{name}")
+        public UselessMessage get(@PathParam("name") final String name) {
+            UselessMessage result = new UselessMessage();
+            result.setMessage("Hello " + name);
+            return result;
+        }
+
+        @POST
+        public String post(final UselessMessage message) {
+            return message.getMessage();
+        }
+
+    } // class Resource
+
+
+    /**
+     * META-INF/services/javax.ws.rs.ext.MessageBodyReader OR META-INF/services/javax.ws.rs.ext.MessageBodyWriter :
+     * org.glassfish.jersey.tests.e2e.server.AbstractDisableMetainfServicesLookupTest$UselessMessageBodyWriter
+     */
+    @Produces("text/plain")
+    @Consumes("text/plain")
+    @Singleton
+    public static class UselessMessageProvider extends AbstractMessageReaderWriterProvider<UselessMessage> {
+
+        public UselessMessageProvider() {
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
+            return type == UselessMessage.class;
+        }
+
+        @Override
+        public UselessMessage readFrom(
+                Class<UselessMessage> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException {
+            return new UselessMessage(readFromAsString(entityStream, mediaType));
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
+            return type == UselessMessage.class;
+        }
+
+        @Override
+        public long getSize(UselessMessage s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return s.getMessage().length();
+        }
+
+        @Override
+        public void writeTo(
+                UselessMessage t,
+                Class<?> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException {
+            writeToAsString(t.getMessage(), entityStream, mediaType);
+        }
+    } // class UselessMessageBodyWriter
+
+
+    public static class UselessMessage {
+
+        private String message;
+
+        public UselessMessage() {
+        }
+
+        public UselessMessage(String message) {
+            this.message = message;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public void setMessage(String message) {
+            this.message = message;
+        }
+
+        @Override
+        public String toString() {
+            return "UselessMessage{"
+                    + "message='" + message + '\''
+                    + '}';
+        }
+    } // class UselessMessage
+
+    private static class MetainfServicesBinder extends AbstractBinder {
+
+        private final Map<String, Object> properties;
+        private final RuntimeType runtimeType;
+
+        public MetainfServicesBinder(final Configuration config) {
+            this.properties = config.getProperties();
+            this.runtimeType = config.getRuntimeType();
+        }
+
+        @Override
+        protected void configure() {
+            // Message Body providers.
+            install(new ServiceFinderBinder<>(MessageBodyReader.class, properties, runtimeType));
+            install(new ServiceFinderBinder<>(MessageBodyWriter.class, properties, runtimeType));
+            // Exception Mappers.
+            install(new ServiceFinderBinder<>(ExceptionMapper.class, properties, runtimeType));
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AllInjectablesTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AllInjectablesTest.java
new file mode 100644
index 0000000..069b031
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AllInjectablesTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2014, 2018 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.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Providers;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.server.ExtendedUriInfo;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author Paul Sandoz
+ */
+public class AllInjectablesTest extends JerseyTest {
+
+    @Path("per-request")
+    public static class PerRequestContextResource {
+        @Context Application app;
+        @Context ResourceContext rc;
+        @Context Configuration config;
+        @Context MessageBodyWorkers mbw;
+        @Context HttpHeaders hs;
+        @Context UriInfo ui;
+        @Context ExtendedUriInfo eui;
+        @Context Request r;
+        @Context SecurityContext sc;
+        @Context Providers p;
+
+        @GET
+        public String get() {
+            assertNotNull(app);
+            assertNotNull(rc);
+            assertNotNull(config);
+            assertNotNull(mbw);
+            assertNotNull(hs);
+            assertNotNull(ui);
+            assertNotNull(eui);
+            assertNotNull(r);
+            assertNotNull(sc);
+            assertNotNull(p);
+            return "GET";
+        }
+    }
+
+    @Path("per-request-constructor")
+    public static class PerRequestContextConstructorParameterResource {
+        public PerRequestContextConstructorParameterResource(@Context final Application app, @Context final ResourceContext rc,
+                @Context final Configuration config, @Context final MessageBodyWorkers mbw, @Context final HttpHeaders hs,
+                @Context final UriInfo ui, @Context final ExtendedUriInfo eui, @Context final Request r,
+                @Context final SecurityContext sc, @Context final Providers p) {
+            assertNotNull(app);
+            assertNotNull(rc);
+            assertNotNull(config);
+            assertNotNull(mbw);
+            assertNotNull(hs);
+            assertNotNull(ui);
+            assertNotNull(eui);
+            assertNotNull(r);
+            assertNotNull(sc);
+            assertNotNull(p);
+        }
+
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Path("per-request-method")
+    public static class PerRequestContextMethodParameterResource {
+        @GET
+        public String get(@Context final Application app, @Context final ResourceContext rc, @Context final Configuration config,
+                          @Context final MessageBodyWorkers mbw, @Context final HttpHeaders hs, @Context final UriInfo ui,
+                          @Context final ExtendedUriInfo eui, @Context final Request r, @Context final SecurityContext sc,
+                          @Context final Providers p) {
+            assertNotNull(app);
+            assertNotNull(rc);
+            assertNotNull(config);
+            assertNotNull(mbw);
+            assertNotNull(hs);
+            assertNotNull(ui);
+            assertNotNull(eui);
+            assertNotNull(r);
+            assertNotNull(sc);
+            assertNotNull(p);
+            return "GET";
+        }
+    }
+
+    @Path("singleton")
+    @Singleton
+    public static class SingletonContextResource {
+        @Context Application app;
+        @Context ResourceContext rc;
+        @Context Configuration config;
+        @Context MessageBodyWorkers mbw;
+        @Context HttpHeaders hs;
+        @Context UriInfo ui;
+        @Context ExtendedUriInfo eui;
+        @Context Request r;
+        @Context SecurityContext sc;
+        @Context Providers p;
+
+        @GET
+        public String get() {
+            assertNotNull(app);
+            assertNotNull(rc);
+            assertNotNull(config);
+            assertNotNull(mbw);
+            assertNotNull(hs);
+            assertNotNull(ui);
+            assertNotNull(eui);
+            assertNotNull(r);
+            assertNotNull(sc);
+            assertNotNull(p);
+            return "GET";
+        }
+    }
+
+    @Path("singleton-constructor")
+    public static class SingletonContextConstructorParameterResource {
+        public SingletonContextConstructorParameterResource(@Context final Application app, @Context final ResourceContext rc,
+                @Context final Configuration config, @Context final MessageBodyWorkers mbw, @Context final HttpHeaders hs,
+                @Context final UriInfo ui, @Context final ExtendedUriInfo eui, @Context final Request r,
+                @Context final SecurityContext sc, @Context final Providers p) {
+            assertNotNull(app);
+            assertNotNull(rc);
+            assertNotNull(config);
+            assertNotNull(mbw);
+            assertNotNull(hs);
+            assertNotNull(ui);
+            assertNotNull(eui);
+            assertNotNull(r);
+            assertNotNull(sc);
+            assertNotNull(p);
+        }
+
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Override
+    public ResourceConfig configure() {
+        return new ResourceConfig(PerRequestContextResource.class, PerRequestContextConstructorParameterResource.class,
+                PerRequestContextMethodParameterResource.class, SingletonContextResource.class,
+                SingletonContextConstructorParameterResource.class);
+    }
+
+    @Test
+    public void testPerRequestInjected() throws IOException {
+        assertEquals("GET", target().path("/per-request").request().get(String.class));
+    }
+
+    @Test
+    public void testPerRequestConstructor() throws IOException {
+        assertEquals("GET", target().path("/per-request-constructor").request().get(String.class));
+    }
+
+    @Test
+    public void testPerRequestMethod() throws IOException {
+        assertEquals("GET", target().path("/per-request-method").request().get(String.class));
+    }
+
+    @Test
+    public void testSingleton() throws IOException {
+        assertEquals("GET", target().path("/singleton").request().get(String.class));
+    }
+
+    @Test
+    public void testSingletonConstructor() throws IOException {
+        assertEquals("GET", target().path("/singleton-constructor").request().get(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AmbigousResourceMethodTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AmbigousResourceMethodTest.java
new file mode 100644
index 0000000..9eb249e
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AmbigousResourceMethodTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test of validation of resources as an end-to-end test.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class AmbigousResourceMethodTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Test
+    public void testRequestToAmbiguousResourceClass() {
+        final String simpleName = TestResource.class.getSimpleName();
+
+        Response response = target().path("test").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals(simpleName + simpleName, response.readEntity(String.class));
+
+        response = target().path("test").request(MediaType.TEXT_HTML_TYPE).get();
+        assertEquals(200, response.getStatus());
+        assertEquals(simpleName, response.readEntity(String.class));
+
+        response = target().path("test").request(MediaType.TEXT_HTML_TYPE).post(Entity.entity("aaaa", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(simpleName + simpleName, response.readEntity(String.class));
+
+        response = target().path("test").request(MediaType.TEXT_HTML_TYPE).post(Entity.entity("aaaa", MediaType.TEXT_HTML_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(simpleName, response.readEntity(String.class));
+    }
+
+    /**
+     * Test ambiguous resource class.
+     */
+    @Path("test")
+    public static class TestResource {
+        @POST
+        public String sub() {
+            return getClass().getSimpleName();
+        }
+
+        @POST
+        @Consumes(MediaType.TEXT_PLAIN)
+        public String subsub() {
+            return sub() + sub();
+        }
+
+        @GET
+        public String get() {
+            return sub();
+        }
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        public String getget() {
+            return subsub();
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest.java
new file mode 100644
index 0000000..6f1df29
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class AppNameBindingTest extends JerseyTest {
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface Global {}
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface NameBoundRequest {}
+
+    @Global
+    public static class GlobalNameBoundFilter implements ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok("global", MediaType.TEXT_PLAIN_TYPE).build());
+        }
+    }
+
+    @NameBoundRequest
+    @Priority(1)
+    public static class NameBoundRequestFilter implements ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok("nameBoundRequest", MediaType.TEXT_PLAIN_TYPE).build());
+        }
+    }
+
+    @Global
+    public static class MyResourceConfig extends ResourceConfig {
+        public MyResourceConfig() {
+            super(
+                    MyResource.class,
+                    GlobalNameBoundFilter.class,
+                    NameBoundRequestFilter.class
+            );
+        }
+    }
+
+    @Path("/")
+    public static class MyResource {
+        @Path("nameBoundRequest")
+        @GET
+        @NameBoundRequest
+        public String getNameBoundRequest() {
+            return "";
+        }
+
+        @Path("global")
+        @GET
+        public String getPostMatching() {
+            return "";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new MyResourceConfig();
+    }
+
+    @Test
+    public void testNameBoundRequest() {
+        test("nameBoundRequest");
+    }
+
+    @Test
+    public void testGlobal() {
+        test("global");
+    }
+
+    private void test(String name) {
+        Response r = target(name).request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals(name, r.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest2.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest2.java
new file mode 100644
index 0000000..a9e9002
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest2.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test {@link NameBinding name binding} annotations on the {@link Application} class.
+ *
+ * @author Miroslav Fuksa
+ */
+public class AppNameBindingTest2 extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApp();
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface FirstGlobalNameBinding {
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface SecondGlobalNameBinding {
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface ThirdGlobalNameBinding {
+    }
+
+    @Provider
+    @FirstGlobalNameBinding
+    public static class AddOneInterceptor implements
+            WriterInterceptor {
+        public void aroundWriteTo(WriterInterceptorContext context)
+                throws IOException, WebApplicationException {
+            String entity = (String) context.getEntity();
+            Integer i = Integer.parseInt(entity);
+            entity = String.valueOf(i + 1);
+            context.setEntity(entity);
+            context.proceed();
+        }
+    }
+
+    @Provider
+    @FirstGlobalNameBinding
+    @SecondGlobalNameBinding
+    public static class AddHundredInterceptor implements
+            WriterInterceptor {
+        public void aroundWriteTo(WriterInterceptorContext context)
+                throws IOException, WebApplicationException {
+            String entity = (String) context.getEntity();
+            Integer i = Integer.parseInt(entity);
+            entity = String.valueOf(i + 100);
+            context.setEntity(entity);
+            context.proceed();
+        }
+    }
+
+    @Provider
+    @FirstGlobalNameBinding
+    @SecondGlobalNameBinding
+    @ThirdGlobalNameBinding
+    public static class AddThousandInterceptor implements
+            WriterInterceptor {
+        public void aroundWriteTo(WriterInterceptorContext context)
+                throws IOException, WebApplicationException {
+            String entity = (String) context.getEntity();
+            Integer i = Integer.parseInt(entity);
+            entity = String.valueOf(i + 1000);
+            context.setEntity(entity);
+            context.proceed();
+        }
+    }
+
+    @Provider
+    @FirstGlobalNameBinding
+    public static class AddTenFilter implements ContainerResponseFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext,
+                           ContainerResponseContext responseContext) throws IOException {
+            String entity = (String) responseContext.getEntity();
+            Integer i = Integer.valueOf(entity);
+            entity = String.valueOf(i + 10);
+            responseContext.setEntity(entity, (Annotation[]) null,
+                    MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+
+    @FirstGlobalNameBinding
+    @SecondGlobalNameBinding
+    public class MyApp extends Application {
+
+        public java.util.Set<java.lang.Class<?>> getClasses() {
+            Set<Class<?>> resources = new HashSet<Class<?>>();
+            resources.add(Resource.class);
+            resources.add(AddOneInterceptor.class);
+            resources.add(AddTenFilter.class);
+            resources.add(AddHundredInterceptor.class);
+            resources.add(AddThousandInterceptor.class);
+            return resources;
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @POST
+        @Path("bind")
+        @FirstGlobalNameBinding
+        @ThirdGlobalNameBinding
+        @Produces("text/plain")
+        public String echoWithBind(String echo) {
+            // note: AddThousandInterceptor will not be triggered even we have here @ThirdGlobalNameBinding. Annotations from
+            // Application class and from resource methods are evaluated separately.
+
+            return echo;
+        }
+
+        @POST
+        @Path("nobind")
+        @Produces("text/plain")
+        public String echoNoBind(String echo) {
+            return echo;
+        }
+    }
+
+    @Test
+    public void testBind() {
+        final Response response = target().path("resource/bind").request(MediaType.TEXT_PLAIN_TYPE).post(
+                Entity.entity(Integer.valueOf(0), MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        final Integer integer = response.readEntity(Integer.class);
+        assertEquals(111, integer.intValue());
+    }
+
+
+    @Test
+    public void testNoBind() {
+        final Response response = target().path("resource/nobind").request(MediaType.TEXT_PLAIN_TYPE).post(
+                Entity.entity(Integer.valueOf(0), MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        final Integer integer = response.readEntity(Integer.class);
+        assertEquals(111, integer.intValue());
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncCallbackTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncCallbackTest.java
new file mode 100644
index 0000000..30df036
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncCallbackTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.ConnectionCallback;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ChunkedOutput;
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ConnectionCallback connection callback}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class AsyncCallbackTest extends JerseyTest {
+
+    public static final AtomicBoolean onDisconnectCalled = new AtomicBoolean(false);
+
+    public static CountDownLatch streamClosedSignal;
+    public static CountDownLatch callbackCalledSignal;
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        @ManagedAsync
+        @Path("outputStream")
+        public void get(@Suspended final AsyncResponse asyncResponse) throws IOException, InterruptedException {
+            asyncResponse.register(MyConnectionCallback.class);
+            final InputStream is = new InputStream() {
+                private int counter = 0;
+
+                @Override
+                public int read() throws IOException {
+                    return 65 + (++counter % 35);
+                }
+
+            };
+            asyncResponse.resume(is);
+        }
+
+        @GET
+        @ManagedAsync
+        @Path("chunked")
+        public void getChunkedOutput(@Suspended AsyncResponse asyncResponse) throws IOException, InterruptedException {
+            asyncResponse.register(MyConnectionCallback.class);
+            ChunkedOutput<String> chunkedOutput = new ChunkedOutput<String>(String.class);
+            asyncResponse.resume(chunkedOutput);
+            for (int i = 0; i < 50000; i++) {
+                chunkedOutput.write("something-");
+            }
+        }
+    }
+
+    public static class TestLatch extends CountDownLatch {
+
+        private final String name;
+        private final int multiplier;
+
+        public TestLatch(int count, String name, int multiplier) {
+            super(count);
+            this.name = name;
+            this.multiplier = multiplier;
+        }
+
+        @Override
+        public void countDown() {
+            super.countDown();
+        }
+
+        @Override
+        public void await() throws InterruptedException {
+            final boolean success = super.await(10 * multiplier, TimeUnit.SECONDS);
+            Assert.assertTrue(
+                    Thread.currentThread().getName() + ": Latch [" + name + "] awaiting -> timeout!!!",
+                    success);
+        }
+    }
+
+    @Before
+    public void setup() {
+        onDisconnectCalled.set(false);
+        streamClosedSignal = new TestLatch(1, "streamClosedSignal", getAsyncTimeoutMultiplier());
+        callbackCalledSignal = new TestLatch(1, "callbackCalledSignal", getAsyncTimeoutMultiplier());
+
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testOutputStream() throws InterruptedException, IOException {
+        _testConnectionCallback("resource/outputStream");
+    }
+
+    @Test
+    public void testChunkedOutput() throws InterruptedException, IOException {
+        _testConnectionCallback("resource/chunked");
+    }
+
+    private void _testConnectionCallback(String path) throws IOException, InterruptedException {
+        final Response response = target().path(path).request().get();
+        final InputStream inputStream = response.readEntity(InputStream.class);
+        for (int i = 0; i < 500; i++) {
+            inputStream.read();
+        }
+        response.close();
+        streamClosedSignal.countDown();
+        callbackCalledSignal.await();
+        Assert.assertTrue(onDisconnectCalled.get());
+    }
+
+    public static class MyConnectionCallback implements ConnectionCallback {
+
+        @Override
+        public void onDisconnect(AsyncResponse disconnected) {
+            onDisconnectCalled.set(true);
+            callbackCalledSignal.countDown();
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java
new file mode 100644
index 0000000..beaff51
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class AsyncResponseTest extends JerseyTest {
+
+    public static CountDownLatch callbackCalledSignal1;
+    public static CountDownLatch callbackCalledSignal2;
+
+    @Override
+    protected Application configure() {
+        callbackCalledSignal1 = new AsyncCallbackTest.TestLatch(3, "cancel() return value1", getAsyncTimeoutMultiplier());
+        callbackCalledSignal2 = new AsyncCallbackTest.TestLatch(3, "cancel() return value2", getAsyncTimeoutMultiplier());
+
+        set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
+
+        return new ResourceConfig(Resource.class, ErrorResource.class, MappedExceptionMapper.class,
+                EntityAnnotationCheckerWriter.class, AsyncMessageBodyProviderResource.class);
+    }
+
+    @Test
+    public void testMultipleCancel() throws InterruptedException, IOException {
+        final Response response = target().path("resource/1").request().get();
+        final InputStream inputStream = response.readEntity(InputStream.class);
+        for (int i = 0; i < 500; i++) {
+            inputStream.read();
+        }
+        response.close();
+        callbackCalledSignal1.await();
+    }
+
+    @Test
+    public void testCancelAfterResume() throws InterruptedException, IOException {
+        final Response response = target().path("resource/2").request().get();
+        final InputStream inputStream = response.readEntity(InputStream.class);
+        for (int i = 0; i < 500; i++) {
+            inputStream.read();
+        }
+        response.close();
+        callbackCalledSignal2.await();
+    }
+
+    @Test
+    public void testResumeWebApplicationException() throws Exception {
+        testResumeException("resumeWebApplicationException", "resumeWebApplicationException");
+    }
+
+    @Test
+    public void testResumeMappedException() throws Exception {
+        testResumeException("resumeMappedException", "resumeMappedException");
+    }
+
+    @Test
+    public void testResumeRuntimeException() throws Exception {
+        testResumeException("resumeRuntimeException", null);
+
+        assertThat(getLastLoggedRecord().getThrown(), instanceOf(RuntimeException.class));
+    }
+
+    @Test
+    public void testResumeCheckedException() throws Exception {
+        testResumeException("resumeCheckedException", null);
+
+        assertThat(getLastLoggedRecord().getThrown(), instanceOf(IOException.class));
+    }
+
+    private void testResumeException(final String path, final String entity) throws Exception {
+        final WebTarget errorResource = target("errorResource");
+
+        final Future<Response> suspended = errorResource.path("suspend").request().async().get();
+        final Response response = errorResource.path(path).request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("ok"));
+
+        final Response suspendedResponse = suspended.get();
+
+        assertThat(suspendedResponse.getStatus(), equalTo(500));
+        if (entity != null) {
+            assertThat(suspendedResponse.readEntity(String.class), equalTo(entity));
+        }
+
+        suspendedResponse.close();
+
+        // Check there is no NPE.
+        for (final LogRecord record : getLoggedRecords()) {
+            final Throwable thrown = record.getThrown();
+            if (thrown != null && thrown instanceof NullPointerException) {
+                fail("Unexpected NPE.");
+            }
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        @Path("1")
+        @ManagedAsync
+        public void get1(@Suspended final AsyncResponse asyncResponse) throws IOException, InterruptedException {
+            if (asyncResponse.cancel()) {
+                callbackCalledSignal1.countDown();
+            }
+            if (asyncResponse.cancel()) {
+                callbackCalledSignal1.countDown();
+            }
+            if (asyncResponse.cancel()) {
+                callbackCalledSignal1.countDown();
+            }
+        }
+
+        @GET
+        @Path("2")
+        @ManagedAsync
+        public void get2(@Suspended final AsyncResponse asyncResponse) throws IOException, InterruptedException {
+            asyncResponse.resume("ok");
+
+            if (!asyncResponse.cancel()) {
+                callbackCalledSignal2.countDown();
+            }
+            if (!asyncResponse.cancel()) {
+                callbackCalledSignal2.countDown();
+            }
+            if (!asyncResponse.cancel()) {
+                callbackCalledSignal2.countDown();
+            }
+        }
+    }
+
+    public static class MappedException extends RuntimeException {
+
+        public MappedException(final String message) {
+            super(message);
+        }
+    }
+
+    public static class MappedExceptionMapper implements ExceptionMapper<MappedException> {
+
+        @Override
+        public Response toResponse(final MappedException exception) {
+            return Response.serverError().entity(exception.getMessage()).build();
+        }
+    }
+
+    @Path("errorResource")
+    public static class ErrorResource {
+
+        private static final BlockingQueue<AsyncResponse> suspended = new ArrayBlockingQueue<AsyncResponse>(1);
+
+        @GET
+        @Path("suspend")
+        public void suspend(@Suspended final AsyncResponse asyncResponse) {
+            suspended.add(asyncResponse);
+        }
+
+        @GET
+        @Path("resumeWebApplicationException")
+        public String resumeWebApplicationException() throws Exception {
+            return resume(new WebApplicationException(Response.serverError().entity("resumeWebApplicationException").build()));
+        }
+
+        @GET
+        @Path("resumeMappedException")
+        public String resumeMappedException() throws Exception {
+            return resume(new MappedException("resumeMappedException"));
+        }
+
+        @GET
+        @Path("resumeRuntimeException")
+        public String resumeRuntimeException() throws Exception {
+            return resume(new RuntimeException("resumeRuntimeException"));
+        }
+
+        @GET
+        @Path("resumeCheckedException")
+        public String resumeCheckedException() throws Exception {
+            return resume(new IOException("resumeCheckedException"));
+        }
+
+        private String resume(final Throwable throwable) throws Exception {
+            return suspended.take().resume(throwable) ? "ok" : "ko";
+        }
+    }
+
+    public static class EntityAnnotationChecker {
+    }
+
+    public static class EntityAnnotationCheckerWriter implements MessageBodyWriter<EntityAnnotationChecker> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(final EntityAnnotationChecker entityAnnotationChecker, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final EntityAnnotationChecker entityAnnotationChecker,
+                            final Class<?> type,
+                            final Type genericType,
+                            final Annotation[] annotations,
+                            final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+
+            final String entity = annotations.length > 0 ? "ok" : "ko";
+
+            entityStream.write(entity.getBytes());
+        }
+    }
+
+    @Path("annotations")
+    public static class AsyncMessageBodyProviderResource {
+
+        private static final BlockingQueue<AsyncResponse> suspended = new ArrayBlockingQueue<AsyncResponse>(1);
+
+        @GET
+        @Path("suspend")
+        public void suspend(@Suspended final AsyncResponse asyncResponse) {
+            suspended.add(asyncResponse);
+        }
+
+        @GET
+        @Path("suspend-resume")
+        public void suspendResume(@Suspended final AsyncResponse asyncResponse) {
+            asyncResponse.resume(new EntityAnnotationChecker());
+        }
+
+        @GET
+        @Path("resume")
+        public String resume() throws Exception {
+            return suspended.take().resume(new EntityAnnotationChecker()) ? "ok" : "ko";
+        }
+    }
+
+    @Test
+    public void testAnnotations() throws Exception {
+        final WebTarget errorResource = target("annotations");
+
+        final Future<Response> suspended = errorResource.path("suspend").request().async().get();
+        final Response response = errorResource.path("resume").request().get();
+        assertThat(response.readEntity(String.class), is("ok"));
+
+        final Response suspendedResponse = suspended.get();
+        assertThat("Entity annotations are not propagated to MBW.", suspendedResponse.readEntity(String.class), is("ok"));
+        suspendedResponse.close();
+    }
+
+    @Test
+    public void testAnnotationsSuspendResume() throws Exception {
+        final Response response = target("annotations").path("suspend-resume").request().async().get().get();
+        assertThat("Entity annotations are not propagated to MBW.", response.readEntity(String.class), is("ok"));
+        response.close();
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamExceptionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamExceptionTest.java
new file mode 100644
index 0000000..fa7f371
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamExceptionTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2015, 2018 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.ws.rs.BeanParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import org.glassfish.jersey.server.ParamException;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Tests the ability to catch WebApplicationException thrown in ParamConverter
+ * used along with BeanParam annotation.
+ *
+ * @author Petr Bouda
+ **/
+public class BeanParamExceptionTest extends JerseyTest {
+
+    private static final String PARAM_NOT_FOUND = "{\"message\":\"This parameter was not found\",\"status\":400}";
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                BeanParamController.class,
+                ModelObjectParamConverter.class,
+                QueryParamErrorMapper.class,
+                JacksonJaxbJsonProvider.class);
+    }
+
+    @Path("/")
+    @Produces("application/json")
+    public static class BeanParamController {
+
+        @GET
+        @Path("/query")
+        public String queryParam(@QueryParam("queryParam") final ModelObject modelObject) {
+            return "Query Param: " + modelObject.toString();
+        }
+
+        @GET
+        @Path("/bean")
+        public String beanParam(@BeanParam final BeanParamObject beanParamObject) {
+            return "Bean Param: " + beanParamObject.getModelObject().toString();
+        }
+
+    }
+
+    @Provider
+    public static class ModelObjectParamConverter implements ParamConverter<ModelObject>, ParamConverterProvider {
+
+        @Override
+        public ModelObject fromString(final String s) {
+            if ("exception".equalsIgnoreCase(s)) {
+                throw new BadParameterException("This parameter was not found");
+            }
+            return new ModelObject(s);
+        }
+
+        @Override
+        public String toString(ModelObject modelObject) {
+            return modelObject.toString();
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> ParamConverter<T> getConverter(Class<T> aClass, Type type, Annotation[] annotations) {
+            return aClass.getName().equals(ModelObject.class.getName()) ? (ParamConverter<T>) this : null;
+        }
+
+    }
+
+    @Provider
+    private static class QueryParamErrorMapper implements ExceptionMapper<ParamException.QueryParamException> {
+
+        @Override
+        public Response toResponse(final ParamException.QueryParamException e) {
+            Response.Status status = Response.Status.BAD_REQUEST;
+            final Throwable cause = e.getCause();
+            if (cause instanceof BadParameterException) {
+                return Response.status(status).entity(new ErrorMessage(status.getStatusCode(), cause.getMessage())).build();
+            }
+            return null;
+        }
+    }
+
+    @Test
+    public void testMarshallExceptionQuery() {
+        Response response = target().path("query").queryParam("queryParam", "exception")
+                .request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        assertEquals(400, response.getStatus());
+        assertEquals(PARAM_NOT_FOUND, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testMarshallExceptionBean() {
+        Response response = target().path("bean").queryParam("queryParam", "exception")
+                .request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        assertEquals(400, response.getStatus());
+        assertEquals(PARAM_NOT_FOUND, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testMarshallModelQuery() {
+        Response response = target().path("query").queryParam("queryParam", "model")
+                .request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("Query Param: model", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testMarshallModelBean() {
+        Response response = target().path("bean").queryParam("queryParam", "model")
+                .request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("Bean Param: model", response.readEntity(String.class));
+    }
+
+    private static class BadParameterException extends RuntimeException {
+
+        public BadParameterException(final String s) {
+            super(s);
+        }
+    }
+
+    public static class BeanParamObject {
+
+        final ModelObject modelObject;
+
+        public BeanParamObject(@QueryParam("queryParam") final ModelObject modelObject) {
+            this.modelObject = modelObject;
+        }
+
+        public ModelObject getModelObject() {
+            return modelObject;
+        }
+    }
+
+    public static class ModelObject {
+
+        private final String privateData;
+
+        public ModelObject(final String privateData) {
+            this.privateData = privateData;
+        }
+
+        @Override
+        public String toString() {
+            return privateData;
+        }
+    }
+
+    public static class ErrorMessage {
+
+        private final String message;
+
+        private final int status;
+
+        public ErrorMessage(final int status, final String message) {
+            this.message = message;
+            this.status = status;
+        }
+
+        @JsonProperty
+        public String getMessage() {
+            return message;
+        }
+
+        @JsonProperty
+        public int getStatus() {
+            return status;
+        }
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamTest.java
new file mode 100644
index 0000000..cb72b3f
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamTest.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2012, 2018 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.util.Arrays;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link BeanParam bean param injections}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class BeanParamTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class,
+                ResourceInitializedBySetter.class);
+    }
+
+    @Test
+    public void compareBeanWithStandardParams() {
+        FullBean bean = getFullBean();
+        Response response = doRequest(bean, "resource/compareBean");
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("true", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testSingleFullBean() {
+        FullBean bean = getFullBean();
+        Response response = doRequest(bean, "resource/singleBean");
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(bean.toString(), response.readEntity(String.class));
+    }
+
+    @Test
+    public void testSingleConstructorInitializedBean() {
+        FullBean bean = getFullBean();
+        Response response = doRequest(bean, "resource/constructorBean");
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(bean.toString(), response.readEntity(String.class));
+    }
+
+    @Test
+    public void testTwoFullBeans() {
+        FullBean bean = getFullBean();
+        Response response = doRequest(bean, "resource/twoBeans");
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(bean.toString() + " / " + bean.toString(), response.readEntity(String.class));
+    }
+
+    @Test
+    public void testTwoDifferentBeans() {
+        FullBean fullBean = getFullBean();
+        SmallBean smallBean = new SmallBean(fullBean);
+        Response response = doRequest(fullBean, "resource/differentBeans");
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(fullBean.toString() + " / " + smallBean.toString(), response.readEntity(String.class));
+    }
+
+    @Test
+    public void testEncodedBean() {
+        FullBean fullBean = getFullBean();
+        fullBean.setQueryParam("encoded/a?&&+./?");
+        fullBean.setMatrixParam("not-encoded/a?&&+./?");
+
+        Response response = doRequest(fullBean, "resource/encodedBean");
+        Assert.assertEquals(200, response.getStatus());
+
+        EncodedBean bean = new EncodedBean("not-encoded/a?&&+./?", "encoded%2Fa%3F%26%26%2B.%2F%3F");
+        Assert.assertEquals(bean.toString(), response.readEntity(String.class));
+    }
+
+    private Response doRequest(FullBean bean, String path) {
+        final Form form = new Form();
+        form.asMap().put("form", Arrays.asList(bean.getFormParam()));
+
+        return target().path(path).path(bean.getPathParam()).matrixParam("matrix",
+                bean.getMatrixParam()).queryParam("query",
+                bean.getQueryParam()).request().header("header", bean.getHeaderParam()).cookie("cookie",
+                bean.getCookie()).post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+    }
+
+    private FullBean getFullBean() {
+        FullBean bean = new FullBean();
+        bean.setPathParam("pathParameter");
+        bean.setMatrixParam("matrixParameter");
+        bean.setQueryParam("queryParameter");
+        bean.setHeaderParam("headerParameter");
+        bean.setCookie("cookieParameter");
+        bean.setFormParam("formParameter");
+        bean.setOverrideRequestNull(true);
+        return bean;
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @POST
+        @Path("singleBean/{path}")
+        public String postBeanParam(@BeanParam FullBean bean) {
+            return bean == null ? "fail: bean param is null!!!" : bean.toString();
+        }
+
+        @POST
+        @Path("constructorBean/{path}")
+        public String constructorBeanParam(@BeanParam ConstructorInitializedBean bean) {
+            return bean == null ? "fail: bean param is null!!!" : bean.toString();
+        }
+
+        @POST
+        @Path("compareBean/{path}")
+        public String compareBeanParam(@BeanParam ConstructorInitializedBean bean, @CookieParam("cookie") String cookie,
+                                       @FormParam("form") String formParam,
+                                       @HeaderParam("header") String headerParam, @MatrixParam("matrix") String matrixParam,
+                                       @QueryParam("query") String queryParam, @PathParam("path") String pathParam,
+                                       @Context Request request) {
+            ConstructorInitializedBean newBean = new ConstructorInitializedBean(cookie, formParam,
+                    headerParam, matrixParam,
+                    queryParam, pathParam, request);
+
+            return String.valueOf(bean.toString().equals(newBean.toString()));
+        }
+
+        @POST
+        @Path("twoBeans/{path}")
+        public String postTwoSameBeans(@BeanParam FullBean bean1, @BeanParam FullBean bean2) {
+            if (bean1 == null) {
+                return "fail: bean1 param is null!!!";
+            }
+            if (bean2 == null) {
+                return "fail: bean2 param is null!!!";
+            }
+            return bean1.toString() + " / " + bean2.toString();
+        }
+
+        @POST
+        @Path("differentBeans/{path}")
+        public String postTwoDifferentBeans(@BeanParam FullBean bean1, @BeanParam SmallBean bean2) {
+            if (bean1 == null) {
+                return "fail: bean1 param is null!!!";
+            }
+            if (bean2 == null) {
+                return "fail: bean2 param is null!!!";
+            }
+            return bean1.toString() + " / " + bean2.toString();
+        }
+
+        @POST
+        @Path("encodedBean/{path}")
+        public String postEncodedParam(@BeanParam EncodedBean bean) {
+            return bean == null ? "fail: bean param is null!!!" : bean.toString();
+        }
+
+    }
+
+    public static class SmallBean {
+
+        @HeaderParam("header")
+        private String headerParam;
+
+        @PathParam("path")
+        private String pathParam;
+
+        public SmallBean(FullBean bean) {
+            headerParam = bean.getHeaderParam();
+            pathParam = bean.getPathParam();
+        }
+
+        public SmallBean() {
+        }
+
+        @Override
+        public String toString() {
+            return "SmallBean{"
+                    + "headerParam='" + headerParam + '\''
+                    + ", pathParam='" + pathParam + '\''
+                    + '}';
+        }
+    }
+
+    public static class EncodedBean {
+
+        @MatrixParam("matrix")
+        private String matrixParam;
+
+        @Encoded
+        @QueryParam("query")
+        private String queryParam;
+
+        public EncodedBean(String matrixParam, String queryParam) {
+            this.matrixParam = matrixParam;
+            this.queryParam = queryParam;
+        }
+
+        public EncodedBean() {
+        }
+
+        @Override
+        public String toString() {
+            return "EncodedBean{"
+                    + "matrixParam='" + matrixParam + '\''
+                    + ", queryParam='" + queryParam + '\''
+                    + '}';
+        }
+    }
+
+    public static class FullBean {
+
+        @HeaderParam("header")
+        private String headerParam;
+
+        @PathParam("path")
+        private String pathParam;
+
+        @MatrixParam("matrix")
+        private String matrixParam;
+
+        @QueryParam("query")
+        private String queryParam;
+
+        @CookieParam("cookie")
+        private String cookie;
+
+        @FormParam("form")
+        private String formParam;
+
+        @Context
+        private Request request;
+
+        private boolean overrideRequestNull;
+
+        public FullBean() {
+        }
+
+        public String getCookie() {
+            return cookie;
+        }
+
+        public void setCookie(String cookie) {
+            this.cookie = cookie;
+        }
+
+        public String getFormParam() {
+            return formParam;
+        }
+
+        public void setFormParam(String formParam) {
+            this.formParam = formParam;
+        }
+
+        public String getHeaderParam() {
+            return headerParam;
+        }
+
+        public void setHeaderParam(String headerParam) {
+            this.headerParam = headerParam;
+        }
+
+        public String getMatrixParam() {
+            return matrixParam;
+        }
+
+        public void setMatrixParam(String matrixParam) {
+            this.matrixParam = matrixParam;
+        }
+
+        public String getPathParam() {
+            return pathParam;
+        }
+
+        public void setPathParam(String pathParam) {
+            this.pathParam = pathParam;
+        }
+
+        public String getQueryParam() {
+            return queryParam;
+        }
+
+        public void setQueryParam(String queryParam) {
+            this.queryParam = queryParam;
+        }
+
+        public Request getRequest() {
+            return request;
+        }
+
+        public void setRequest(Request request) {
+            this.request = request;
+        }
+
+        public boolean isOverrideRequestNull() {
+            return overrideRequestNull;
+        }
+
+        public void setOverrideRequestNull(boolean overrideRequestNull) {
+            this.overrideRequestNull = overrideRequestNull;
+        }
+
+        private String requestToString() {
+            if (overrideRequestNull) {
+                return "not-null";
+            } else {
+                return request == null ? "null" : "not-null";
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Bean{"
+                    + "cookie='" + cookie + '\''
+                    + ", formParam='" + formParam + '\''
+                    + ", headerParam='" + headerParam + '\''
+                    + ", matrixParam='" + matrixParam + '\''
+                    + ", pathParam='" + pathParam + '\''
+                    + ", queryParam='" + queryParam + '\''
+                    + ", request='" + requestToString() + "'"
+                    + '}';
+        }
+    }
+
+    public static class ConstructorInitializedBean {
+
+        private String headerParam;
+        private String pathParam;
+        private String matrixParam;
+        private String queryParam;
+        private String cookie;
+        private String formParam;
+        private Request request;
+
+        public ConstructorInitializedBean(@CookieParam("cookie") String cookie, @FormParam("form") String formParam,
+                                          @HeaderParam("header") String headerParam, @MatrixParam("matrix") String matrixParam,
+                                          @QueryParam("query") String queryParam, @PathParam("path") String pathParam,
+                                          @Context Request request) {
+            this.cookie = cookie;
+            this.formParam = formParam;
+            this.headerParam = headerParam;
+            this.matrixParam = matrixParam;
+            this.queryParam = queryParam;
+            this.pathParam = pathParam;
+            this.request = request;
+        }
+
+        private boolean overrideRequestNull;
+
+        public String getCookie() {
+            return cookie;
+        }
+
+        public void setCookie(String cookie) {
+            this.cookie = cookie;
+        }
+
+        public String getFormParam() {
+            return formParam;
+        }
+
+        public void setFormParam(String formParam) {
+            this.formParam = formParam;
+        }
+
+        public String getHeaderParam() {
+            return headerParam;
+        }
+
+        public void setHeaderParam(String headerParam) {
+            this.headerParam = headerParam;
+        }
+
+        public String getMatrixParam() {
+            return matrixParam;
+        }
+
+        public void setMatrixParam(String matrixParam) {
+            this.matrixParam = matrixParam;
+        }
+
+        public String getPathParam() {
+            return pathParam;
+        }
+
+        public void setPathParam(String pathParam) {
+            this.pathParam = pathParam;
+        }
+
+        public String getQueryParam() {
+            return queryParam;
+        }
+
+        public void setQueryParam(String queryParam) {
+            this.queryParam = queryParam;
+        }
+
+        public Request getRequest() {
+            return request;
+        }
+
+        public void setRequest(Request request) {
+            this.request = request;
+        }
+
+        public boolean isOverrideRequestNull() {
+            return overrideRequestNull;
+        }
+
+        public void setOverrideRequestNull(boolean overrideRequestNull) {
+            this.overrideRequestNull = overrideRequestNull;
+        }
+
+        private String requestToString() {
+            if (overrideRequestNull) {
+                return "not-null";
+            } else {
+                return request == null ? "null" : "not-null";
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "Bean{"
+                    + "cookie='" + cookie + '\''
+                    + ", formParam='" + formParam + '\''
+                    + ", headerParam='" + headerParam + '\''
+                    + ", matrixParam='" + matrixParam + '\''
+                    + ", pathParam='" + pathParam + '\''
+                    + ", queryParam='" + queryParam + '\''
+                    + ", request='" + requestToString() + "'"
+                    + '}';
+        }
+    }
+
+    @Path("resource-setter")
+    public static class ResourceInitializedBySetter {
+
+        private FullBean fullBean;
+
+        @BeanParam
+        public void setFullBean(FullBean fullBean) {
+            this.fullBean = fullBean;
+        }
+
+        @POST
+        @Path("{path}")
+        public String post() {
+            return fullBean.toString();
+        }
+    }
+
+    @Test
+    public void testResourceInitializedBySetter() {
+        FullBean bean = getFullBean();
+        final Response response = doRequest(bean, "resource-setter");
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(bean.toString(), response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BroadcasterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BroadcasterTest.java
new file mode 100644
index 0000000..9a967cc
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/BroadcasterTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.Broadcaster;
+import org.glassfish.jersey.server.BroadcasterListener;
+import org.glassfish.jersey.server.ChunkedOutput;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class BroadcasterTest extends JerseyTest {
+    static Broadcaster<String> broadcaster = new Broadcaster<String>() {
+        @Override
+        public void onClose(ChunkedOutput<String> stringChunkedOutput) {
+            closedOutputs.add(stringChunkedOutput);
+        }
+    };
+
+    static List<ChunkedOutput<String>> outputs = new ArrayList<>();
+    static List<ChunkedOutput<String>> closedOutputs = new ArrayList<>();
+    static int listenerClosed = 0;
+
+    @Path("/test")
+    public static class MyResource {
+        @GET
+        public ChunkedOutput<String> get() {
+            ChunkedOutput<String> result = new ChunkedOutput<String>() {};
+
+            // write something to ensure the client does not get blocked on waiting for the first byte
+            try {
+                result.write("firstChunk");
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+            outputs.add(result);
+            broadcaster.add(result);
+            return result;
+        }
+
+        @POST
+        public String post(String text) {
+            broadcaster.broadcast(text);
+            return text;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class);
+    }
+
+    @Test
+    public void testBroadcaster() throws IOException {
+        InputStream is1 = getChunkStream();
+        InputStream is2 = getChunkStream();
+        InputStream is3 = getChunkStream();
+        InputStream is4 = getChunkStream();
+
+        target("test").request().post(Entity.text("text1"));
+        checkClosed(0);
+        checkStream("firstChunktext1", is1, is2, is3, is4);
+
+        outputs.remove(0).close();
+
+        target("test").request().post(Entity.text("text2"));
+        checkStream("text2", is2, is3, is4);
+        checkClosed(1);
+
+        outputs.remove(0).close();
+
+        BroadcasterListener<String> bl = new BroadcasterListener<String>() {
+            @Override
+            public void onException(ChunkedOutput<String> stringChunkedResponse, Exception exception) {
+            }
+
+            @Override
+            public void onClose(ChunkedOutput<String> stringChunkedResponse) {
+                listenerClosed++;
+            }
+        };
+
+        broadcaster.add(bl);
+
+        target("test").request().post(Entity.text("text3"));
+        checkClosed(2);
+        assertEquals(1, listenerClosed);
+
+        broadcaster.remove(bl);
+        broadcaster.closeAll();
+
+        checkClosed(4);
+        assertEquals(1, listenerClosed);
+
+        checkStream("text3", is3, is4);
+    }
+
+    private InputStream getChunkStream() {
+        return target("test").request().get(InputStream.class);
+    }
+
+    private void checkStream(String golden, InputStream... inputStreams) throws IOException {
+        byte[] bytes = golden.getBytes();
+        byte[] entity = new byte[bytes.length];
+        for (InputStream is : inputStreams) {
+            int bytesRead = 0;
+            int previous = 0;
+            while ((bytesRead += is.read(entity, bytesRead, entity.length - bytesRead)) < entity.length
+                    && previous != bytesRead) {
+                previous = bytesRead;
+            }
+            assertEquals(golden, new String(entity));
+        }
+    }
+
+    private void checkClosed(int count) {
+        assertEquals("Closed count does not match", count, closedOutputs.size());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
new file mode 100644
index 0000000..29aec13
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.client.ChunkedInput;
+import org.glassfish.jersey.server.ChunkedOutput;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Chunked input/output tests.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ChunkedInputOutputTest extends JerseyTest {
+    private static final Logger LOGGER = Logger.getLogger(ChunkedInputOutputTest.class.getName());
+
+    /**
+     * Test resource.
+     */
+    @Path("/test")
+    public static class TestResource {
+        /**
+         * Get chunk stream.
+         *
+         * @return chunk stream.
+         */
+        @GET
+        public ChunkedOutput<String> get() {
+            final ChunkedOutput<String> output = new ChunkedOutput<>(String.class, "\r\n");
+
+            new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        output.write("test");
+                        output.write("test");
+                        output.write("test");
+                    } catch (final IOException e) {
+                        LOGGER.log(Level.SEVERE, "Error writing chunk.", e);
+                    } finally {
+                        try {
+                            output.close();
+                        } catch (final IOException e) {
+                            LOGGER.log(Level.INFO, "Error closing chunked output.", e);
+                        }
+                    }
+                }
+            }.start();
+
+            return output;
+        }
+
+        /**
+         * Get chunk stream with an attached interceptor.
+         *
+         * @return intercepted chunk stream.
+         */
+        @GET
+        @Path("intercepted")
+        @Intercepted
+        public ChunkedOutput<String> interceptedGet() {
+            return get();
+        }
+    }
+
+    /**
+     * Test interceptor binding.
+     */
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public static @interface Intercepted {
+
+    }
+
+    /**
+     * Test interceptor - counts number of interception as well as number of wrapper output stream method calls.
+     */
+    @Intercepted
+    public static class TestWriterInterceptor implements WriterInterceptor {
+
+        private static final AtomicInteger interceptCounter = new AtomicInteger(0);
+        private static final AtomicInteger writeCounter = new AtomicInteger(0);
+        private static final AtomicInteger flushCounter = new AtomicInteger(0);
+        private static final AtomicInteger closeCounter = new AtomicInteger(0);
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            interceptCounter.incrementAndGet();
+            final OutputStream out = context.getOutputStream();
+            context.setOutputStream(new OutputStream() {
+                @Override
+                public void write(final int b) throws IOException {
+                    writeCounter.incrementAndGet();
+                    out.write(b);
+                }
+
+                @Override
+                public void write(final byte[] b) throws IOException {
+                    writeCounter.incrementAndGet();
+                    out.write(b);
+                }
+
+                @Override
+                public void write(final byte[] b, final int off, final int len) throws IOException {
+                    writeCounter.incrementAndGet();
+                    out.write(b, off, len);
+                }
+
+                @Override
+                public void flush() throws IOException {
+                    flushCounter.incrementAndGet();
+                    out.flush();
+                }
+
+                @Override
+                public void close() throws IOException {
+                    closeCounter.incrementAndGet();
+                    out.close();
+                }
+            });
+            context.proceed();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class, TestWriterInterceptor.class);
+    }
+
+    /**
+     * Test retrieving chunked response stream as a single response string.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testChunkedOutputToSingleString() throws Exception {
+        final String response = target().path("test").request().get(String.class);
+
+        assertEquals("Unexpected value of chunked response unmarshalled as a single string.",
+                "test\r\ntest\r\ntest\r\n", response);
+    }
+
+    /**
+     * Test retrieving chunked response stream sequentially as individual chunks using chunked input.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testChunkedOutputToChunkInput() throws Exception {
+        final ChunkedInput<String> input = target().path("test").request().get(new GenericType<ChunkedInput<String>>() {
+        });
+
+        int counter = 0;
+        String chunk;
+        while ((chunk = input.read()) != null) {
+            assertEquals("Unexpected value of chunk " + counter, "test", chunk);
+            counter++;
+        }
+
+        assertEquals("Unexpected numbed of received chunks.", 3, counter);
+    }
+
+    /**
+     * Test retrieving intercepted chunked response stream sequentially as individual chunks using chunked input.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testInterceptedChunkedOutputToChunkInput() throws Exception {
+        final ChunkedInput<String> input = target().path("test/intercepted")
+                .request().get(new GenericType<ChunkedInput<String>>() {
+                });
+
+        int counter = 0;
+        String chunk;
+        while ((chunk = input.read()) != null) {
+            assertEquals("Unexpected value of chunk " + counter, "test", chunk);
+            counter++;
+        }
+
+        assertThat("Unexpected numbed of received chunks.",
+                counter, equalTo(3));
+
+        assertThat("Unexpected number of chunked output interceptions.",
+                TestWriterInterceptor.interceptCounter.get(), equalTo(1));
+        assertThat("Unexpected number of intercepted output write calls.",
+                TestWriterInterceptor.writeCounter.get(), greaterThanOrEqualTo(1));
+        assertThat("Unexpected number of intercepted output flush calls.",
+                TestWriterInterceptor.flushCounter.get(), greaterThanOrEqualTo(3));
+        assertThat("Unexpected number of intercepted output close calls.",
+                TestWriterInterceptor.closeCounter.get(), equalTo(1));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ClientResponseOnServerTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ClientResponseOnServerTest.java
new file mode 100644
index 0000000..2642876
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ClientResponseOnServerTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.Uri;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for support of client-side response in the server-side resource implementation.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ClientResponseOnServerTest extends JerseyTest {
+
+    @Path("root")
+    public static class RootResource {
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Path("response")
+        public Response getResponse(@Uri("internal/response") WebTarget target) {
+            // returns client-side response instance
+            return target.request(MediaType.TEXT_PLAIN).get();
+        }
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Path("error")
+        public String getError(@Uri("internal/error") WebTarget target) {
+            // throws WebApplicationException with an error response
+            return target.request(MediaType.TEXT_PLAIN).get(String.class);
+        }
+    }
+
+    @Path("internal")
+    public static class InternalResource {
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Path("response")
+        public String getResponse() {
+            return "response";
+        }
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        @Path("error")
+        public Response getError() {
+            // Testing for a cross-stack support of a completely custom status code.
+            return Response.status(699).type(MediaType.TEXT_PLAIN).entity("error").build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                RootResource.class,
+                InternalResource.class
+        );
+    }
+
+    @Test
+    public void testClientResponseUsageOnServer() {
+        final WebTarget target = target("root/{type}");
+
+        Response response;
+
+        response = target.resolveTemplate("type", "response").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        assertEquals("response", response.readEntity(String.class));
+
+        response = target.resolveTemplate("type", "error").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(699, response.getStatus());
+        assertEquals("error", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CloseableTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CloseableTest.java
new file mode 100644
index 0000000..89bf38d
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CloseableTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014, 2018 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.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.CloseableService;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Marc Hadley
+ */
+public class CloseableTest extends JerseyTest {
+    private static CountDownLatch perRequestCdl = new CountDownLatch(1);
+    private static CountDownLatch singletonCdl = new CountDownLatch(1);
+
+    @Path("per-request")
+    public static class PerRequestResource implements Closeable {
+        static boolean isClosed;
+
+        @Context
+        CloseableService cs;
+
+        @GET
+        public String doGet() {
+            isClosed = false;
+            cs.add(this);
+            return "ok";
+        }
+
+        public void close() throws IOException {
+            isClosed = true;
+            perRequestCdl.countDown();
+        }
+    }
+
+    @Path("singleton")
+    @Singleton
+    public static class SingletonResource extends PerRequestResource {
+        @Override
+        public void close() {
+            isClosed = true;
+            singletonCdl.countDown();
+        }
+    }
+
+    @Override
+    public ResourceConfig configure() {
+        return new ResourceConfig(PerRequestResource.class, SingletonResource.class);
+    }
+
+    @Test
+    public void testPerRequest() throws InterruptedException {
+        target().path("per-request").request().get(String.class);
+        perRequestCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertTrue(PerRequestResource.isClosed);
+    }
+
+    @Test
+    public void testSingleton() throws InterruptedException {
+        target().path("singleton").request().get(String.class);
+        perRequestCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertTrue(SingletonResource.isClosed);
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
new file mode 100644
index 0000000..c1ce740
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2017, 2018 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.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class CompletionStageTest extends JerseyTest {
+
+    static final String ENTITY = "entity";
+    // delay of async operations in seconds.
+    static final int DELAY = 1;
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(CompletionStageResource.class);
+    }
+
+    @Test
+    public void testGetCompleted() {
+        Response response = target("cs/completed").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(ENTITY));
+    }
+
+    @Test
+    public void testGetException400() {
+        Response response = target("cs/exception400").request().get();
+
+        assertThat(response.getStatus(), is(400));
+    }
+
+    @Test
+    public void testGetException405() {
+        Response response = target("cs/exception405").request().get();
+
+        assertThat(response.getStatus(), is(405));
+    }
+
+    @Test
+    public void testGetCancelled() {
+        Response response = target("cs/cancelled").request().get();
+
+        assertThat(response.getStatus(), is(503));
+    }
+
+    @Test
+    public void testGetCompletedAsync() {
+        Response response = target("cs/completedAsync").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(ENTITY));
+    }
+
+    @Test
+    public void testGetException400Async() {
+        Response response = target("cs/exception400Async").request().get();
+
+        assertThat(response.getStatus(), is(400));
+    }
+
+    @Test
+    public void testGetException405Async() {
+        Response response = target("cs/exception405Async").request().get();
+
+        assertThat(response.getStatus(), is(405));
+    }
+
+    @Test
+    public void testGetCancelledAsync() {
+        Response response = target("cs/cancelledAsync").request().get();
+
+        assertThat(response.getStatus(), is(503));
+    }
+
+    @Test
+    public void testGetCustomCompleted() {
+        Response response = target("cs/custom").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(ENTITY));
+    }
+
+    @Test
+    public void testGetCustomAsync() {
+        Response response = target("cs/customAsync").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(ENTITY));
+    }
+
+    @Path("/cs")
+    public static class CompletionStageResource {
+
+        private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
+
+        @GET
+        @Path("/completed")
+        public CompletionStage<String> getCompleted() {
+            return CompletableFuture.completedFuture(ENTITY);
+        }
+
+        @GET
+        @Path("/exception400")
+        public CompletionStage<String> getException400() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            cs.completeExceptionally(new WebApplicationException(400));
+
+            return cs;
+        }
+
+        @GET
+        @Path("/exception405")
+        public CompletionStage<String> getException405() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            cs.completeExceptionally(new WebApplicationException(405));
+
+            return cs;
+        }
+
+        @GET
+        @Path("/cancelled")
+        public CompletionStage<String> getCancelled() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            cs.cancel(true);
+
+            return cs;
+        }
+
+        @GET
+        @Path("/completedAsync")
+        public CompletionStage<String> getCompletedAsync() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            delaySubmit(() -> cs.complete(ENTITY));
+            return cs;
+        }
+
+        @GET
+        @Path("/exception400Async")
+        public CompletionStage<String> getException400Async() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            delaySubmit(() -> cs.completeExceptionally(new WebApplicationException(400)));
+
+            return cs;
+        }
+
+        @GET
+        @Path("/exception405Async")
+        public CompletionStage<String> getException405Async() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            delaySubmit(() -> cs.completeExceptionally(new WebApplicationException(405)));
+
+            return cs;
+        }
+
+        @GET
+        @Path("/cancelledAsync")
+        public CompletionStage<String> getCancelledAsync() {
+            CompletableFuture<String> cs = new CompletableFuture<>();
+            delaySubmit(() -> cs.cancel(true));
+
+            return cs;
+        }
+
+        /**
+         * Return completed CompletionStage which doesn't support #toCompletableFuture().
+         *
+         * @return CompletionStage which doesn't support #toCompletableFuture().
+         */
+        @GET
+        @Path("/custom")
+        public CompletionStage<String> getCustomCompletionStage() {
+            return new CustomCompletionStage<>(CompletableFuture.completedFuture(ENTITY));
+        }
+
+        /**
+         * Return uncompleted CompletionStage which doesn't support #toCompletableFuture().
+         *
+         * @return CompletionStage which doesn't support #toCompletableFuture().
+         */
+        @GET
+        @Path("/customAsync")
+        public CompletionStage<String> getCustomCompletionStageAsync() {
+            CompletableFuture<String> cf = new CompletableFuture<>();
+            CustomCompletionStage<String> cs = new CustomCompletionStage<>(cf);
+            delaySubmit(() -> cf.complete(ENTITY));
+
+            return cs;
+        }
+
+        private void delaySubmit(Runnable runnable) {
+            EXECUTOR_SERVICE.submit(() -> {
+                try {
+                    Thread.sleep(DELAY * 1000);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+
+                runnable.run();
+            });
+        }
+    }
+
+    private static class CustomCompletionStage<T> implements CompletionStage<T> {
+
+        private final CompletionStage<T> completedFuture;
+
+        CustomCompletionStage(CompletionStage<T> completedFuture) {
+            this.completedFuture = completedFuture;
+        }
+
+        @Override
+        public <U> CompletionStage<U> thenApply(Function<? super T, ? extends U> fn) {
+            return completedFuture.thenApply(fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> thenApplyAsync(Function<? super T, ? extends U> fn) {
+            return completedFuture.thenApplyAsync(fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> thenApplyAsync(Function<? super T, ? extends U> fn, Executor executor) {
+            return completedFuture.thenApplyAsync(fn, executor);
+        }
+
+        @Override
+        public CompletionStage<Void> thenAccept(Consumer<? super T> action) {
+            return completedFuture.thenAccept(action);
+        }
+
+        @Override
+        public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action) {
+            return completedFuture.thenAcceptAsync(action);
+        }
+
+        @Override
+        public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor) {
+            return completedFuture.thenAcceptAsync(action, executor);
+        }
+
+        @Override
+        public CompletionStage<Void> thenRun(Runnable action) {
+            return completedFuture.thenRun(action);
+        }
+
+        @Override
+        public CompletionStage<Void> thenRunAsync(Runnable action) {
+            return completedFuture.thenRunAsync(action);
+        }
+
+        @Override
+        public CompletionStage<Void> thenRunAsync(Runnable action, Executor executor) {
+            return completedFuture.thenRunAsync(action, executor);
+        }
+
+        @Override
+        public <U, V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,
+                                                     BiFunction<? super T, ? super U, ? extends V> fn) {
+            return completedFuture.thenCombine(other, fn);
+        }
+
+        @Override
+        public <U, V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
+                                                          BiFunction<? super T, ? super U, ? extends V> fn) {
+            return completedFuture.thenCombineAsync(other, fn);
+        }
+
+        @Override
+        public <U, V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
+                                                          BiFunction<? super T, ? super U, ? extends V> fn, Executor executor) {
+            return completedFuture.thenCombineAsync(other, fn, executor);
+        }
+
+        @Override
+        public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,
+                                                        BiConsumer<? super T, ? super U> action) {
+            return completedFuture.thenAcceptBoth(other, action);
+        }
+
+        @Override
+        public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
+                                                             BiConsumer<? super T, ? super U> action) {
+            return completedFuture.thenAcceptBothAsync(other, action);
+        }
+
+        @Override
+        public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
+                                                             BiConsumer<? super T, ? super U> action, Executor executor) {
+            return completedFuture.thenAcceptBothAsync(other, action, executor);
+        }
+
+        @Override
+        public CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action) {
+            return completedFuture.runAfterBoth(other, action);
+        }
+
+        @Override
+        public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action) {
+            return completedFuture.runAfterBothAsync(other, action);
+        }
+
+        @Override
+        public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor) {
+            return completedFuture.runAfterBothAsync(other, action, executor);
+        }
+
+        @Override
+        public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) {
+            return completedFuture.applyToEither(other, fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) {
+            return completedFuture.applyToEitherAsync(other, fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,
+                                                         Function<? super T, U> fn,
+                                                         Executor executor) {
+            return completedFuture.applyToEitherAsync(other, fn, executor);
+        }
+
+        @Override
+        public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) {
+            return completedFuture.acceptEither(other, action);
+        }
+
+        @Override
+        public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) {
+            return completedFuture.acceptEitherAsync(other, action);
+        }
+
+        @Override
+        public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
+                                                       Consumer<? super T> action,
+                                                       Executor executor) {
+            return completedFuture.acceptEitherAsync(other, action, executor);
+        }
+
+        @Override
+        public CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action) {
+            return completedFuture.runAfterEither(other, action);
+        }
+
+        @Override
+        public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action) {
+            return completedFuture.runAfterEitherAsync(other, action);
+        }
+
+        @Override
+        public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor) {
+            return completedFuture.runAfterEitherAsync(other, action, executor);
+        }
+
+        @Override
+        public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
+            return completedFuture.thenCompose(fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) {
+            return completedFuture.thenComposeAsync(fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
+                                                       Executor executor) {
+            return completedFuture.thenComposeAsync(fn, executor);
+        }
+
+        @Override
+        public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn) {
+            return completedFuture.exceptionally(fn);
+        }
+
+        @Override
+        public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) {
+            return completedFuture.whenComplete(action);
+        }
+
+        @Override
+        public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) {
+            return completedFuture.whenCompleteAsync(action);
+        }
+
+        @Override
+        public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) {
+            return completedFuture.whenCompleteAsync(action, executor);
+        }
+
+        @Override
+        public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {
+            return completedFuture.handle(fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) {
+            return completedFuture.handleAsync(fn);
+        }
+
+        @Override
+        public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
+            return completedFuture.handleAsync(fn, executor);
+        }
+
+        @Override
+        public CompletableFuture<T> toCompletableFuture() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ContainerListenerRegistrationAsProvidersTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ContainerListenerRegistrationAsProvidersTest.java
new file mode 100644
index 0000000..bc46bee
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ContainerListenerRegistrationAsProvidersTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ContainerListenerRegistrationAsProvidersTest extends JerseyTest {
+
+    MyListener listener;
+
+    @Override
+    public ResourceConfig configure() {
+        listener = new MyListener();
+        final ResourceConfig result = new ResourceConfig(One.class, YetAnotherListener.class).registerInstances(listener);
+        return result;
+    }
+
+    static class YetAnotherListener extends AbstractContainerLifecycleListener {
+
+        static boolean started;
+
+        @Override
+        public void onStartup(Container container) {
+            started = true;
+        }
+    }
+
+    static class MyListener extends AbstractContainerLifecycleListener {
+
+        boolean startupInvoked;
+
+        @Override
+        public void onStartup(Container container) {
+            startupInvoked = true;
+        }
+    }
+
+    @Test
+    public void testListener() {
+        assertEquals("whatever", target().path("doesNotMatter").request().get().readEntity(String.class));
+        assertTrue(listener.startupInvoked);
+        assertTrue(YetAnotherListener.started);
+    }
+
+    @Path("doesNotMatter")
+    public static class One {
+        @GET
+        public String get() {
+            return "whatever";
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ContentNegotiationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ContentNegotiationTest.java
new file mode 100644
index 0000000..77732c7
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ContentNegotiationTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2013, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.internal.HttpUrlConnector;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests determining media type of the response (especially that qs quality parameter is respected when
+ * more media types are defined on the resource method).
+ *
+ * @author Miroslav Fuksa
+ */
+public class ContentNegotiationTest extends JerseyTest {
+
+    @Path("persons")
+    public static class MyResource {
+        private static final Person[] LIST = new Person[] {
+                new Person("Penny", 1),
+                new Person("Howard", 2),
+                new Person("Sheldon", 3)
+        };
+
+        @GET
+        @Produces({"application/xml;qs=0.75", "application/json;qs=1.0"})
+        public Person[] getList() {
+            return LIST;
+        }
+
+        @GET
+        @Produces({"application/json;qs=1", "application/xml;qs=0.75"})
+        @Path("reordered")
+        public Person[] getListReordered() {
+            return LIST;
+        }
+
+        @GET
+        @Produces({"application/json;qs=0.75", "application/xml;qs=1"})
+        @Path("inverted")
+        public Person[] getListInverted() {
+            return LIST;
+        }
+
+
+        @GET
+        @Produces({"application/xml;qs=0.75", "application/json;qs=0.9", "unknown/hello;qs=1.0"})
+        @Path("unkownMT")
+        public Person[] getListWithUnkownType() {
+            return LIST;
+        }
+
+        @GET
+        @Produces({"application/json", "application/xml", "text/plain"})
+        @Path("shouldPickFirstJson")
+        public Person[] getJsonArrayUnlessOtherwiseSpecified() {
+            return LIST;
+        }
+
+        @GET
+        @Produces({"application/xml", "text/plain", "application/json"})
+        @Path("shouldPickFirstXml")
+        public Person[] getXmlUnlessOtherwiseSpecified() {
+            return LIST;
+        }
+
+        @GET
+        @Produces("application/json;qs=0.75")
+        @Path("twoMethodsOneEndpoint")
+        public Person[] getJsonArray() {
+            return LIST;
+        }
+
+        @GET
+        @Produces("application/xml;qs=1")
+        @Path("twoMethodsOneEndpoint")
+        public Person[] getXml() {
+            return LIST;
+        }
+    }
+
+    @XmlRootElement
+    public static class Person {
+        private String name;
+        private int age;
+
+        public Person() {
+        }
+
+        public Person(String name, int age) {
+            this.name = name;
+            this.age = age;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getAge() {
+            return age;
+        }
+
+        public void setAge(int age) {
+            this.age = age;
+        }
+
+        @Override
+        public String toString() {
+            return name + "(" + age + ")";
+        }
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class);
+    }
+
+    /**
+     * {@link HttpUrlConnector} by default adds some media types
+     * to the Accept header if we don't specify them.
+     */
+    @Test
+    public void testWithoutDefinedRequestedMediaType() {
+        WebTarget target = target().path("/persons");
+        Response response = target.request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testWithoutDefinedRequestedMediaTypeAndTwoMethods() {
+        //We can not rely on method declaration ordering:
+        //From Class javadoc: "The elements in the returned array are not sorted and are not in any particular order."
+        //If there are same endpoints it is necessary to use quality parameter to ensure ordering.
+        Response response = target().path("/persons/twoMethodsOneEndpoint").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_XML_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testWithoutDefinedRequestedMediaTypeOrQualityModifiersJson() {
+        Response response = target().path("/persons/shouldPickFirstJson").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testWithoutDefinedRequestedMediaTypeOrQualityModifiersXml() {
+        Response response = target().path("/persons/shouldPickFirstXml").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_XML_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void test() {
+        WebTarget target = target().path("/persons");
+        Response response = target.request(MediaType.WILDCARD).get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testInverted() {
+        WebTarget target = target().path("/persons/inverted");
+        Response response = target.request(MediaType.WILDCARD).get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_XML_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testInvertedWithJSONPreferredByClient() {
+        WebTarget target = target().path("/persons/inverted");
+        Response response = target.request("application/json;q=1.0", "application/xml;q=0.8").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testReordered() {
+        WebTarget target = target().path("/persons/reordered");
+        Response response = target.request(MediaType.WILDCARD).get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+    }
+
+    /**
+     * Client and server prefers "unknown/hello" but there is no MBW on server to write such a type. Therefore
+     * this type is ignored and "application/xml" is chosen (because it is the second preferred type by the client).
+     */
+    @Test
+    public void testWithUnknownTypePreferredByClient() {
+        WebTarget target = target().path("/persons/reordered");
+        Response response = target.request("application/json;q=0.8", "application/xml;q=0.9",
+                "unknown/hello;qs=1.0").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(MediaType.APPLICATION_XML_TYPE, response.getMediaType());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesApplicationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesApplicationTest.java
new file mode 100644
index 0000000..494bff6
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesApplicationTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2012, 2018 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.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.inject.hk2.Hk2RequestScope;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.internal.inject.Binder;
+import org.glassfish.jersey.internal.inject.Bindings;
+import org.glassfish.jersey.internal.inject.ClassBinding;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.InstanceBinding;
+import org.glassfish.jersey.process.internal.RequestScope;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class CustomInjectablesApplicationTest extends JerseyTest {
+
+    public static class MyApplication extends Application {
+
+        public static Set<Class<?>> classes = new HashSet<Class<?>>() {{
+            add(Resource.class);
+        }};
+
+        @Inject
+        public MyApplication(InjectionManager injectionManager) {
+            System.out.println("Registering injectables...");
+            ClassBinding<MyInjectablePerRequest> injectClassRequest =
+                    Bindings.serviceAsContract(MyInjectablePerRequest.class)
+                            .in(RequestScoped.class);
+
+            ClassBinding<MyInjectableSingleton> injectClassSingleton =
+                    Bindings.serviceAsContract(MyInjectableSingleton.class)
+                            .in(Singleton.class);
+
+            InstanceBinding<MyInjectableSingleton> injectInstanceSingleton =
+                    Bindings.serviceAsContract(new MyInjectableSingleton());
+
+            ClassBinding<MyInjectablePerRequest> injectQualifiedClassRequest =
+                    Bindings.serviceAsContract(MyInjectablePerRequest.class)
+                            .qualifiedBy(new MyQualifierImpl())
+                            .in(RequestScoped.class);
+
+            injectionManager.register(Arrays.asList(
+                    injectClassRequest, injectClassSingleton, injectInstanceSingleton, injectQualifiedClassRequest));
+        }
+
+        @Override
+        public Set<Class<?>> getClasses() {
+            return classes;
+        }
+    }
+
+    public static class MyInjectablePerRequest {
+        public int i = 0;
+    }
+
+    @Singleton
+    public static class MyInjectableSingleton {
+        public int i = 0;
+    }
+
+    @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    @Qualifier
+    public static @interface MyQualifier {
+
+    }
+
+    private static class MyQualifierImpl extends AnnotationLiteral<MyQualifier> implements MyQualifier {
+    }
+
+    @Path("/")
+    public static class Resource {
+        @Inject
+        MyInjectablePerRequest myInjectablePerRequest;
+
+        @Inject
+        MyInjectableSingleton myInjectableSingleton;
+
+        @Inject
+        @MyQualifier
+        MyInjectablePerRequest myInjectablePerRequest2;
+
+        @GET
+        @Path("/perrequest")
+        public String getAndIncPerRequest() {
+            return Integer.valueOf(++myInjectablePerRequest.i).toString();
+        }
+
+        @GET
+        @Path("/perrequestCustomQualifier")
+        public String getAndIncPerRequest2() {
+            return Integer.valueOf(++myInjectablePerRequest2.i).toString();
+        }
+
+        @GET
+        @Path("/singleton")
+        @Produces("text/plain")
+        public String getAndIncSingleton() {
+            System.out.println(myInjectableSingleton);
+            return Integer.valueOf(++myInjectableSingleton.i).toString();
+        }
+    }
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        // If strategy is not IMMEDIATE then test will fail even before @Before setup method invocation.
+        // It has no other reason then just run the tests in IMMEDIATE strategy.
+        if (Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            return DeploymentContext.newInstance(MyApplication.class);
+        } else {
+            return DeploymentContext.newInstance(new ResourceConfig());
+        }
+    }
+
+    @Test
+    public void testPerRequest() throws Exception {
+        final javax.ws.rs.client.WebTarget perrequest = target().path("perrequest");
+
+        assertEquals("1", perrequest.request().get(String.class));
+        assertEquals("1", perrequest.request().get(String.class));
+        assertEquals("1", perrequest.request().get(String.class));
+    }
+
+    @Test
+    public void testSingleton() throws Exception {
+        final javax.ws.rs.client.WebTarget perrequest = target().path("singleton");
+
+        assertEquals("1", perrequest.request().get(String.class));
+        assertEquals("2", perrequest.request().get(String.class));
+        assertEquals("3", perrequest.request().get(String.class));
+    }
+
+    @Test
+    public void testCustomQualifier() throws Exception {
+        final javax.ws.rs.client.WebTarget perrequestCustomAnnotation = target().path("perrequestCustomQualifier");
+
+        assertEquals("1", perrequestCustomAnnotation.request().get(String.class));
+        assertEquals("1", perrequestCustomAnnotation.request().get(String.class));
+        assertEquals("1", perrequestCustomAnnotation.request().get(String.class));
+    }
+
+    @Test
+    public void plainHK2Test() throws Exception {
+        final InjectionManager injectionManager = Injections.createInjectionManager(
+                new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(Hk2RequestScope.class).to(RequestScope.class).in(Singleton.class);
+                        bindAsContract(MyInjectablePerRequest.class).in(RequestScoped.class);
+                        bindAsContract(MyInjectableSingleton.class).in(Singleton.class);
+                    }
+                });
+        injectionManager.completeRegistration();
+
+        final RequestScope requestScope = injectionManager.getInstance(RequestScope.class);
+
+        final MyInjectableSingleton myInjectableSingleton = injectionManager.getInstance(MyInjectableSingleton.class);
+        assertEquals(myInjectableSingleton, injectionManager.getInstance(MyInjectableSingleton.class));
+
+        final MyInjectablePerRequest myInjectablePerRequest = requestScope.runInScope(new Callable<MyInjectablePerRequest>() {
+
+            @Override
+            public MyInjectablePerRequest call() throws Exception {
+                final MyInjectablePerRequest myInjectablePerRequest = injectionManager.getInstance(MyInjectablePerRequest.class);
+                assertEquals(myInjectablePerRequest, injectionManager.getInstance(MyInjectablePerRequest.class));
+                return myInjectablePerRequest;
+            }
+        });
+
+        requestScope.runInScope(new Runnable() {
+
+            @Override
+            public void run() {
+                assertNotSame(myInjectablePerRequest, injectionManager.getInstance(MyInjectablePerRequest.class));
+            }
+        });
+
+    }
+
+    @Test
+    public void plainHK2DynamicTest() throws Exception {
+        Binder binder = new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(Hk2RequestScope.class)
+                        .to(RequestScope.class)
+                        .in(Singleton.class);
+
+                bindAsContract(MyInjectablePerRequest.class)
+                        .in(RequestScoped.class);
+
+                bindAsContract(MyInjectableSingleton.class)
+                        .in(Singleton.class);
+            }
+        };
+        InjectionManager injectionManager = Injections.createInjectionManager();
+        injectionManager.register(binder);
+        injectionManager.completeRegistration();
+
+        final RequestScope requestScope = injectionManager.getInstance(RequestScope.class);
+
+        final MyInjectableSingleton myInjectableSingleton = injectionManager.getInstance(MyInjectableSingleton.class);
+        assertEquals(myInjectableSingleton, injectionManager.getInstance(MyInjectableSingleton.class));
+
+        final MyInjectablePerRequest myInjectablePerRequest = requestScope.runInScope(new Callable<MyInjectablePerRequest>() {
+            @Override
+            public MyInjectablePerRequest call() throws Exception {
+                final MyInjectablePerRequest myInjectablePerRequest = injectionManager.getInstance(MyInjectablePerRequest.class);
+                assertEquals(myInjectablePerRequest, injectionManager.getInstance(MyInjectablePerRequest.class));
+                return myInjectablePerRequest;
+            }
+        });
+
+        requestScope.runInScope(new Runnable() {
+            @Override
+            public void run() {
+                assertNotSame(myInjectablePerRequest, injectionManager.getInstance(MyInjectablePerRequest.class));
+            }
+        });
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesResourceConfigTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesResourceConfigTest.java
new file mode 100644
index 0000000..836fb4d
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesResourceConfigTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2012, 2018 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.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class CustomInjectablesResourceConfigTest extends JerseyTest {
+
+    public static class MyHK2Binder extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            // request scope binding
+            bindAsContract(MyInjectablePerRequest.class).in(RequestScoped.class);
+
+            // singleton binding
+            bindAsContract(MyInjectableSingleton.class).in(Singleton.class);
+
+            // singleton instance binding
+            bind(new MyInjectableSingleton()).to(MyInjectableSingleton.class);
+
+            // request scope binding with specified custom annotation
+            bindAsContract(MyInjectablePerRequest.class).qualifiedBy(new MyQualifierImpl()).in(RequestScoped.class);
+        }
+    }
+
+    public static class MyInjectablePerRequest {
+        public int i = 0;
+    }
+
+    @Singleton
+    public static class MyInjectableSingleton {
+        public int i = 0;
+    }
+
+    @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    @Qualifier
+    public static @interface MyQualifier {
+
+    }
+
+    private static class MyQualifierImpl extends AnnotationLiteral<MyQualifier> implements MyQualifier {
+    }
+
+    @Path("/")
+    public static class Resource {
+        @Inject
+        MyInjectablePerRequest myInjectablePerRequest;
+
+        @Inject
+        MyInjectableSingleton myInjectableSingleton;
+
+        @Inject
+        @MyQualifier
+        MyInjectablePerRequest myInjectablePerRequest2;
+
+        @GET
+        @Path("/perrequest")
+        public String getAndIncPerRequest() {
+            return Integer.valueOf(++myInjectablePerRequest.i).toString();
+        }
+
+        @GET
+        @Path("/perrequestCustomQualifier")
+        public String getAndIncPerRequest2() {
+            return Integer.valueOf(++myInjectablePerRequest2.i).toString();
+        }
+
+        @GET
+        @Path("/singleton")
+        @Produces("text/plain")
+        public String getAndIncSingleton() {
+            System.out.println(myInjectableSingleton);
+            return Integer.valueOf(++myInjectableSingleton.i).toString();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig rc = new ResourceConfig();
+        rc.registerClasses(Resource.class);
+        rc.register(new MyHK2Binder());
+
+        return rc;
+    }
+
+    @Test
+    public void testPerRequest() throws Exception {
+        final javax.ws.rs.client.WebTarget perRequest = target().path("perrequest");
+
+        assertEquals("1", perRequest.request().get(String.class));
+        assertEquals("1", perRequest.request().get(String.class));
+        assertEquals("1", perRequest.request().get(String.class));
+    }
+
+    @Test
+    public void testSingleton() throws Exception {
+        final javax.ws.rs.client.WebTarget perRequest = target().path("singleton");
+
+        assertEquals("1", perRequest.request().get(String.class));
+        assertEquals("2", perRequest.request().get(String.class));
+        assertEquals("3", perRequest.request().get(String.class));
+    }
+
+    @Test
+    public void testCustomAnnotation() throws Exception {
+        final javax.ws.rs.client.WebTarget perRequestCustomAnnotation = target().path("perrequestCustomQualifier");
+
+        assertEquals("1", perRequestCustomAnnotation.request().get(String.class));
+        assertEquals("1", perRequestCustomAnnotation.request().get(String.class));
+        assertEquals("1", perRequestCustomAnnotation.request().get(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomMultivaluedMapProviderTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomMultivaluedMapProviderTest.java
new file mode 100644
index 0000000..e9088df
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomMultivaluedMapProviderTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+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 org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Tests that the Custom MultivaluedMap provider overrides the default
+ * EntityProvider
+ *
+ * @author Petr Bouda
+ */
+public class CustomMultivaluedMapProviderTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                TestResource.class,
+                CustomMultivaluedMapProvider.class);
+    }
+
+    @Provider
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public static class CustomMultivaluedMapProvider implements MessageBodyReader<MultivaluedMap<String, String>> {
+
+        @Override
+        public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return MultivaluedMap.class.isAssignableFrom(type);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public MultivaluedMap readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
+            MultivaluedMap map = new MultivaluedHashMap();
+            map.add(getClass().getSimpleName(), getClass().getSimpleName().replace("Provider", "Reader"));
+            return map;
+        }
+    }
+
+    @Path("resource")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public static class TestResource {
+
+        @POST
+        @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+        public MultivaluedMap<String, String> map(MultivaluedMap<String, String> map) {
+            return map;
+        }
+
+    }
+
+    @Test
+    public void testNullFormParam() {
+        Response response = target("resource").request()
+                .post(Entity.entity("map", MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("CustomMultivaluedMapProvider=CustomMultivaluedMapReader", response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedFormParamTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedFormParamTest.java
new file mode 100644
index 0000000..34d0379
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedFormParamTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 2018 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.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Proper encoding of Form params
+ *
+ * @author Petr Bouda
+ */
+public class EncodedFormParamTest extends JerseyTest {
+
+    @Path("encoded")
+    public static class UrlEncodedResource {
+
+        @Encoded
+        @FormParam("name")
+        private String name;
+
+        @FormParam("name")
+        private String otherName;
+
+        @POST
+        @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+        public String get(@FormParam("name") List<String> name)  {
+            return name.toString() + " " + this.name;
+        }
+
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(UrlEncodedResource.class);
+    }
+
+    @Test
+    public void testEncodedParam() {
+        Response result = target().path("encoded").request()
+                .post(Entity.entity("name&name=George", MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+        assertEquals("[null, George] null", result.readEntity(String.class));
+    }
+}
+
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedSlashInPathSegmentTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedSlashInPathSegmentTest.java
new file mode 100644
index 0000000..1d828f8
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedSlashInPathSegmentTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for JERSEY-1167.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class EncodedSlashInPathSegmentTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(EncodedSlashResource.class);
+    }
+
+    @Path("/test/{p}")
+    public static class EncodedSlashResource {
+
+        @GET
+        public String get(@PathParam("p") String p) {
+            return p;
+        }
+    }
+
+    @Test
+    public void testEncodedSlashInPathParam() throws Exception {
+
+        final Response response = target().path("test/one%2Ftwo").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("one/two", response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EntityExpansionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EntityExpansionTest.java
new file mode 100644
index 0000000..fee7de4
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/EntityExpansionTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2013, 2018 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.util.logging.Logger;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.glassfish.jersey.internal.util.SaxHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Tests properties configuring secure sax parsing.
+ *
+ * @author Miroslav Fuksa
+ */
+public class EntityExpansionTest extends JerseyTest {
+
+    private static final Logger LOG = Logger.getLogger(EntityExpansionTest.class.getName());
+    private static boolean isXdk = false;
+
+    @Override
+    protected Application configure() {
+        System.setProperty("entityExpansionLimit", "10");
+        System.setProperty("elementAttributeLimit", "1");
+
+        final ResourceConfig resourceConfig = new ResourceConfig(TestResource.class, BadRequestMapper.class);
+        return resourceConfig;
+    }
+
+    public static class BadRequestMapper implements ExceptionMapper<BadRequestException> {
+        @Override
+        public Response toResponse(BadRequestException exception) {
+            Throwable t = exception;
+            while (t != null && t.getClass() != SAXParseException.class) {
+                t = t.getCause();
+            }
+            if (t != null) {
+                return Response.ok().entity("PASSED:" + t.getMessage()).build();
+            }
+            return Response.status(500).build();
+        }
+    }
+
+    @Path("resource")
+    public static class TestResource {
+
+        @POST
+        public String post(TestBean bean) {
+            return bean.getInput();
+        }
+
+    }
+
+    @XmlRootElement()
+    @XmlAccessorType(value = XmlAccessType.FIELD)
+    public static class TestBean {
+        @XmlElement
+        private String input;
+
+        @XmlAttribute
+        private String str;
+
+        @XmlAttribute
+        private String str2;
+
+        @XmlAttribute
+        private String str3;
+
+
+        public String getStr2() {
+            return str2;
+        }
+
+        public void setStr2(String str2) {
+            this.str2 = str2;
+        }
+
+        public String getStr() {
+            return str;
+        }
+
+        public void setStr(String str) {
+            this.str = str;
+        }
+
+
+        public String getStr3() {
+            return str3;
+        }
+
+        public void setStr3(String str3) {
+            this.str3 = str3;
+        }
+
+        public String getInput() {
+            return input;
+        }
+
+        public void setInput(String input) {
+            this.input = input;
+        }
+    }
+
+    @BeforeClass
+    public static void setXdkFlag() {
+        // XDK SAXParser does not support this feature, so the test has to be skipped if XDK detected.
+        if (SaxHelper.isXdkParserFactory(SAXParserFactory.newInstance())) {
+            LOG.warning("XDK SAXParser detected, FEATURE_SECURE_PROCESSING is not supported. Tests will be skipped.");
+            isXdk = true;
+        }
+        Assume.assumeTrue(!isXdk);
+    }
+
+    @Test
+    public void testEntityExpansion() {
+        String str = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+                + "\n<!DOCTYPE lolz [\n"
+                + "  <!ENTITY lol \"lollollollollollollol[...]\">\n"
+                + "  <!ENTITY lol2 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n"
+                + "  <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n"
+                + "]>\n"
+                + "<testBean><input>&lol3;</input></testBean>";
+
+        final Response response = target().path("resource").request().post(Entity.entity(str, MediaType.APPLICATION_XML));
+        Assert.assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        Assert.assertTrue(entity.startsWith("PASSED"));
+    }
+
+    @Test
+    public void testMaxAttributes() {
+        String str = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+                + "<testBean str=\"aaa\" str2=\"bbb\" str3=\"ccc\"><input>test</input></testBean>";
+        final Response response = target().path("resource").request().post(Entity.entity(str, MediaType.APPLICATION_XML));
+        Assert.assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        Assert.assertTrue(entity.startsWith("PASSED"));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java
new file mode 100644
index 0000000..6d33ae7
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.logging.Level;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Make sure exceptions, that are not mapped to responses get logged.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ExceptionLoggingTest extends JerseyTest {
+
+    private static class MyCheckedException extends Exception {}
+
+    private static class MyRuntimeException extends RuntimeException {}
+
+    @Path("/")
+    public static class ExceptionResource {
+
+        @Path("runtime")
+        public String runtimeException() {
+            throw new MyRuntimeException();
+        }
+
+        @Path("checked")
+        public String checkedException() throws MyCheckedException {
+            throw new MyCheckedException();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
+
+        return new ResourceConfig(ExceptionResource.class, Writer.class, Resource.class);
+    }
+
+    @Test
+    public void testRuntime() throws Exception {
+        final Response response = target().path("runtime").request().get();
+        assertEquals(500, response.getStatus());
+        assertEquals(getLastLoggedRecord().getThrown().getClass(), MyRuntimeException.class);
+    }
+
+    @Test
+    public void testChecked() throws Exception {
+        final Response response = target().path("checked").request().get();
+        assertEquals(500, response.getStatus());
+        assertEquals(getLastLoggedRecord().getThrown().getClass(), MyCheckedException.class);
+    }
+
+    @Provider
+    @Produces(MediaType.WILDCARD)
+    public static class Writer implements MessageBodyWriter<ExceptionLoggingTestPOJO> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == ExceptionLoggingTestPOJO.class;
+        }
+
+        @Override
+        public long getSize(ExceptionLoggingTestPOJO entityForReader, Class<?> type, Type genericType,
+                            Annotation[] annotations, MediaType mediaType) {
+            return 0;
+        }
+
+        @Override
+        public void writeTo(ExceptionLoggingTestPOJO entityForReader, Class<?> type, Type genericType,
+                            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            throw new RuntimeException("test");
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @Path("entity")
+        @GET
+        public ExceptionLoggingTestPOJO entity() {
+            return new ExceptionLoggingTestPOJO();
+        }
+    }
+
+    @Test
+    public void testReaderFails() throws Exception {
+        final Response response = target().path("resource/entity").request().get();
+        assertEquals(500, response.getStatus());
+
+        assertEquals(getLastLoggedRecord().getThrown().getMessage(), "test");
+    }
+
+    static class ExceptionLoggingTestPOJO {
+
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPriorityTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPriorityTest.java
new file mode 100644
index 0000000..cf25234
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPriorityTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2017, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ExceptionMapperPriorityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ExceptionMapperPriorityResource.class,
+                                  MyFirstExceptionMapper.class,
+                                  MySecondExceptionMapper.class,
+                                  MyThirdExceptionMapper.class);
+    }
+
+    @Test
+    public void priorityTest() {
+        String response = target().request().get(String.class);
+
+        assertThat(response, is(MySecondExceptionMapper.class.getName()));
+    }
+
+    @Path("/")
+    public static class ExceptionMapperPriorityResource {
+
+        @GET
+        public String get() throws MyException {
+            throw new MyException();
+        }
+    }
+
+    public static class MyException extends Exception {
+
+    }
+
+    @Provider
+    @Priority(300)
+    public static class MyFirstExceptionMapper implements ExceptionMapper<MyException> {
+
+        @Override
+        public Response toResponse(MyException exception) {
+            return Response.ok(MyFirstExceptionMapper.class.getName()).build();
+        }
+    }
+
+    @Provider
+    @Priority(100)
+    public static class MySecondExceptionMapper implements ExceptionMapper<MyException> {
+
+        @Override
+        public Response toResponse(MyException exception) {
+            return Response.ok(MySecondExceptionMapper.class.getName()).build();
+        }
+    }
+
+    @Provider
+    @Priority(200)
+    public static class MyThirdExceptionMapper implements ExceptionMapper<MyException> {
+
+        @Override
+        public Response toResponse(MyException exception) {
+            return Response.ok(MyThirdExceptionMapper.class.getName()).build();
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPropagationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPropagationTest.java
new file mode 100644
index 0000000..ef9daf7
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPropagationTest.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test exception mappers handling exceptions thrown from different part of code.
+ * <p/>
+ * There are more tests for exception mappers. This one focuses on testing that exceptions
+ * thrown from providers are correctly propagated to the exception mapper.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+@RunWith(ConcurrentRunner.class)
+public class ExceptionMapperPropagationTest extends JerseyTest {
+
+    public static final String EXCEPTION_TYPE = "exception-type";
+    public static final String MAPPED = "-mapped-";
+    public static final String MAPPED_WAE = "-wae-";
+    public static final String PROVIDER = "provider";
+
+    public static class TestRuntimeException extends RuntimeException {
+
+        public TestRuntimeException(String message) {
+            super(message);
+        }
+
+    }
+
+    public static class TestCheckedException extends Exception {
+
+        public TestCheckedException(String message) {
+            super(message);
+        }
+
+    }
+
+    public static class TestWebAppException extends WebApplicationException {
+
+        public TestWebAppException(String message, Response response) {
+            super(message, response);
+        }
+
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                UniversalThrowableMapper.class,
+                ExceptionResource.class,
+                TestResponseFilter.class,
+                TestRequestFilter.class,
+                WebAppMapper.class,
+                TestMBR.class,
+                TestMBW.class,
+                TestWriterInterceptor.class,
+                TestReaderInterceptor.class
+        );
+    }
+
+    public static class UniversalThrowableMapper implements ExceptionMapper<Throwable> {
+
+        @Override
+        public Response toResponse(Throwable exception) {
+            return Response.ok().entity(exception.getClass().getSimpleName() + MAPPED + exception.getMessage()).build();
+        }
+
+    }
+
+    public static class WebAppMapper implements ExceptionMapper<TestWebAppException> {
+
+        @Override
+        public Response toResponse(TestWebAppException exception) {
+            final Response response = exception.getResponse();
+            return Response.status(response.getStatus())
+                    .entity(exception.getClass().getSimpleName() + MAPPED_WAE + exception.getMessage())
+                    .build();
+        }
+    }
+
+    public static void throwException(String exceptionType, String provider, Class<?> currentClass) throws Throwable {
+
+        if (shouldThrow(exceptionType, provider, currentClass)) {
+            if (exceptionType.equals(TestCheckedException.class.getSimpleName())) {
+                throw new TestCheckedException(provider);
+            } else if (exceptionType.equals(TestRuntimeException.class.getSimpleName())) {
+                throw new TestRuntimeException(provider);
+            } else if (exceptionType.equals(TestWebAppException.class.getSimpleName())) {
+                throw new TestWebAppException(provider, Response.ok().build());
+            } else if (exceptionType.equals(ProcessingException.class.getSimpleName())) {
+                throw new ProcessingException(provider);
+            }
+        }
+    }
+
+    private static boolean shouldThrow(String exceptionType, String provider, Class<?> currentClass) {
+        return exceptionType != null && currentClass.getSimpleName().equals(provider);
+    }
+
+    @Path("exception")
+    public static class ExceptionResource {
+
+        @Path("general")
+        @POST
+        public Response post(@HeaderParam(EXCEPTION_TYPE) String exceptionType,
+                             @HeaderParam(PROVIDER) String provider, String entity) throws Throwable {
+            throwException(exceptionType, provider, this.getClass());
+            return Response.ok().entity("exception/general#get called")
+                    .header(EXCEPTION_TYPE, exceptionType)
+                    .header(PROVIDER, provider).build();
+        }
+
+        @Path("sub")
+        public SubResourceLocator subResourceLocator(@HeaderParam(EXCEPTION_TYPE) String exceptionType,
+                                                     @HeaderParam(PROVIDER) String provider) throws Throwable {
+            throwException(exceptionType, provider, this.getClass());
+            return new SubResourceLocator();
+        }
+
+    }
+
+    public static class SubResourceLocator {
+
+        @POST
+        public String post(@HeaderParam(EXCEPTION_TYPE) String exceptionType,
+                           @HeaderParam(PROVIDER) String provider, String entity) throws Throwable {
+            throwException(exceptionType, provider, this.getClass());
+            return "sub-get";
+        }
+    }
+
+    public static class TestResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            final String exceptionType = responseContext.getHeaderString(EXCEPTION_TYPE);
+            final String provider = responseContext.getHeaderString(PROVIDER);
+
+            throwRuntimeExceptionAndIO(exceptionType, this.getClass(), provider);
+        }
+    }
+
+    public static class TestRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            final String exceptionType = requestContext.getHeaderString(EXCEPTION_TYPE);
+            final String provider = requestContext.getHeaderString(PROVIDER);
+
+            throwRuntimeExceptionAndIO(exceptionType, this.getClass(), provider);
+        }
+    }
+
+    @Consumes(MediaType.TEXT_PLAIN)
+    public static class TestMBR implements MessageBodyReader<String> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public String readFrom(Class<String> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                               MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException,
+                WebApplicationException {
+            final String exceptionType = httpHeaders.getFirst(EXCEPTION_TYPE);
+            final String provider = httpHeaders.getFirst(PROVIDER);
+            throwRuntimeExceptionAndIO(exceptionType, this.getClass(), provider);
+
+            byte b;
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            while ((b = (byte) entityStream.read()) != -1) {
+                baos.write(b);
+            }
+            return new String(baos.toByteArray());
+        }
+    }
+
+    @Produces({"text/plain", "*/*"})
+    public static class TestMBW implements MessageBodyWriter<String> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return 0;
+        }
+
+        @Override
+        public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            final String exceptionType = (String) httpHeaders.getFirst(EXCEPTION_TYPE);
+            final String provider = (String) httpHeaders.getFirst(PROVIDER);
+            throwRuntimeExceptionAndIO(exceptionType, this.getClass(), provider);
+
+            entityStream.write(s.getBytes());
+            entityStream.flush();
+        }
+    }
+
+    @Consumes(MediaType.TEXT_PLAIN)
+    public static class TestReaderInterceptor implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final String exceptionType = context.getHeaders().getFirst(EXCEPTION_TYPE);
+            final String provider = context.getHeaders().getFirst(PROVIDER);
+
+            throwRuntimeExceptionAndIO(exceptionType, this.getClass(), provider);
+            return context.proceed();
+        }
+    }
+
+    public static class TestWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final String exceptionType = (String) context.getHeaders().getFirst(EXCEPTION_TYPE);
+            final String provider = (String) context.getHeaders().getFirst(PROVIDER);
+
+            throwRuntimeExceptionAndIO(exceptionType, this.getClass(), provider);
+            context.proceed();
+        }
+    }
+
+    private static void throwRuntimeExceptionAndIO(String exceptionType, Class<?> providerClass, String provider)
+            throws IOException {
+        if (shouldThrow(exceptionType, provider, providerClass)) {
+            if (exceptionType.equals(TestRuntimeException.class.getSimpleName())) {
+                throw new TestRuntimeException(providerClass.getSimpleName());
+            } else if (exceptionType.equals(IOException.class.getSimpleName())) {
+                throw new IOException(providerClass.getSimpleName());
+            } else if (exceptionType.equals(TestWebAppException.class.getSimpleName())) {
+                throw new TestWebAppException(providerClass.getSimpleName(), Response.ok().build());
+            } else if (exceptionType.equals(ProcessingException.class.getSimpleName())) {
+                throw new ProcessingException(provider);
+            }
+        }
+    }
+
+    // Resource
+    @Test
+    public void testCheckedExceptionInResource() {
+        _test(TestCheckedException.class, ExceptionResource.class);
+    }
+
+    @Test
+    public void testRuntimeExceptionInResource() {
+        _test(TestRuntimeException.class, ExceptionResource.class);
+    }
+
+    @Test
+    public void testWebApplicationExceptionInResource() {
+        _testWae(ExceptionResource.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInResource() {
+        _test(ProcessingException.class, ExceptionResource.class);
+    }
+
+    // Sub resource
+    @Test
+    public void testCheckedExceptionInSubResourceLocatorMethod() {
+        _test(TestCheckedException.class, ExceptionResource.class, "exception/sub");
+    }
+
+    @Test
+    public void testRuntimeExceptionInSubResourceLocatorMethod() {
+        _test(TestRuntimeException.class, ExceptionResource.class, "exception/sub");
+    }
+
+    @Test
+    public void testWaeInSubResourceLocatorMethod() {
+        _testWae(ExceptionResource.class, "exception/sub");
+    }
+
+    @Test
+    public void testProcessingExceptionInSubResourceLocatorMethod() {
+        _test(ProcessingException.class, ExceptionResource.class, "exception/sub");
+    }
+
+    @Test
+    public void testCheckedExceptionInSubResource() {
+        _test(TestCheckedException.class, SubResourceLocator.class, "exception/sub");
+    }
+
+    @Test
+    public void testRuntimeExceptionInSubResource() {
+        _test(TestRuntimeException.class, SubResourceLocator.class, "exception/sub");
+    }
+
+    @Test
+    public void testWaeInSubResource() {
+        _testWae(SubResourceLocator.class, "exception/sub");
+    }
+
+    @Test
+    public void testProcessingExceptionInSubResource() {
+        _test(ProcessingException.class, SubResourceLocator.class, "exception/sub");
+    }
+
+    // response filters
+    @Test
+    public void testRuntimeExceptionInResponseFilter() {
+        _test(TestRuntimeException.class, TestResponseFilter.class);
+    }
+
+    @Test
+    public void testIOExceptionInResponseFilter() {
+        _test(IOException.class, TestResponseFilter.class);
+    }
+
+    @Test
+    public void testWaeInResponseFilter() {
+        _testWae(TestResponseFilter.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInResponseFilter() {
+        _test(ProcessingException.class, TestResponseFilter.class);
+    }
+
+    // response filters
+    @Test
+    public void testRuntimeExceptionInRequestFilter() {
+        _test(TestRuntimeException.class, TestRequestFilter.class);
+    }
+
+    @Test
+    public void testIOExceptionInRequestFilter() {
+        _test(IOException.class, TestRequestFilter.class);
+    }
+
+    @Test
+    public void testWaeInRequestFilter() {
+        _testWae(TestRequestFilter.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInRequestFilter() {
+        _test(ProcessingException.class, TestRequestFilter.class);
+    }
+
+    // MBR/W
+    @Test
+    public void testRuntimeExceptionInMBW() {
+        _test(TestRuntimeException.class, TestMBW.class);
+    }
+
+    @Test
+    public void testIOExceptionInMBW() {
+        _test(IOException.class, TestMBW.class);
+    }
+
+    @Test
+    public void testWaeInMBW() {
+        _testWae(TestMBW.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInMBW() {
+        _test(ProcessingException.class, TestMBW.class);
+    }
+
+    @Test
+    public void testRuntimeExceptionInMBR() {
+        _test(TestRuntimeException.class, TestMBR.class);
+    }
+
+    @Test
+    public void testIOExceptionInMBR() {
+        _test(IOException.class, TestMBR.class);
+    }
+
+    @Test
+    public void testWaeInMBR() {
+        _testWae(TestMBR.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInMBR() {
+        _test(ProcessingException.class, TestMBR.class);
+    }
+
+    // interceptors
+    @Test
+    public void testRuntimeExceptionInReaderInterceptor() {
+        _test(TestRuntimeException.class, TestReaderInterceptor.class);
+    }
+
+    @Test
+    public void testIOExceptionInReaderInterceptor() {
+        _test(IOException.class, TestReaderInterceptor.class);
+    }
+
+    @Test
+    public void testWaeInReaderInterceptor() {
+        _testWae(TestReaderInterceptor.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInReaderInterceptor() {
+        _test(ProcessingException.class, TestReaderInterceptor.class);
+    }
+
+    @Test
+    public void testRuntimeExceptionInWriterInterceptor() {
+        _test(TestRuntimeException.class, TestWriterInterceptor.class);
+    }
+
+    @Test
+    public void testIOExceptionInWriterInterceptor() {
+        _test(IOException.class, TestWriterInterceptor.class);
+    }
+
+    @Test
+    public void testWaeInWriterInterceptor() {
+        _testWae(TestWriterInterceptor.class);
+    }
+
+    @Test
+    public void testProcessingExceptionInWriterInterceptor() {
+        _test(ProcessingException.class, TestWriterInterceptor.class);
+    }
+
+    private void _test(Class<?> exceptionClass, Class<?> providerClass, String path) {
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        final Response response = target(path).request()
+                .header(EXCEPTION_TYPE, exceptionClass.getSimpleName()).header(PROVIDER, providerClass.getSimpleName())
+                .accept(MediaType.TEXT_PLAIN_TYPE)
+                .post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(exceptionClass.getSimpleName() + MAPPED + providerClass.getSimpleName(), response.readEntity(String.class));
+    }
+
+    private void _testWae(Class<?> providerClass) {
+        final String path = "exception/general";
+        _testWae(providerClass, path);
+
+    }
+
+    private void _testWae(Class<?> providerClass, String path) {
+        final Class<?> exceptionClass = TestWebAppException.class;
+
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        final Response response = target(path).request()
+                .header(EXCEPTION_TYPE, exceptionClass.getSimpleName()).header(PROVIDER, providerClass.getSimpleName())
+                .accept(MediaType.TEXT_PLAIN_TYPE)
+                .post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(exceptionClass.getSimpleName() + MAPPED_WAE + providerClass.getSimpleName(),
+                response.readEntity(String.class));
+    }
+
+    private void _test(Class<?> exceptionClass, Class<?> providerClass) {
+        final String path = "exception/general";
+        _test(exceptionClass, providerClass, path);
+    }
+
+    // sub resource locator
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java
new file mode 100644
index 0000000..6ebbac5
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests throwing exceptions in {@link MessageBodyReader} and {@link MessageBodyWriter}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class ExceptionMapperTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                Resource.class,
+                MyMessageBodyWritter.class,
+                MyMessageBodyReader.class,
+                ClientErrorExceptionMapper.class,
+                MyExceptionMapper.class,
+                ThrowableMapper.class,
+                MyExceptionMapperCauseAnotherException.class,
+                // JERSEY-1515
+                TestResource.class,
+                VisibilityExceptionMapper.class,
+                // JERSEY-1525
+                ExceptionTestResource.class,
+                ExceptionThrowingFilter.class,
+                IOExceptionMapper.class,
+                IOExceptionMessageReader.class,
+                IOExceptionResource.class,
+                MessageBodyProviderNotFoundResource.class,
+                ProviderNotFoundExceptionMapper.class,
+                // JERSEY-1887
+                Jersey1887Resource.class,
+                Jersey1887ExceptionMapperImpl.class,
+                // JERSEY-2382
+                Jersey2382Resource.class,
+                Jersey2382ExceptionMapper.class,
+                Jersey2382Provider.class
+        );
+    }
+
+    @Test
+    public void testReaderThrowsException() {
+        Response res = target().path("test").request("test/test").header("reader-exception", "throw")
+                .post(Entity.entity("post", "test/test"));
+        assertEquals(200, res.getStatus());
+        final String entity = res.readEntity(String.class);
+        assertEquals("reader-exception-mapper", entity);
+    }
+
+    @Test
+    public void testWriterThrowsExceptionBeforeFirstBytesAreWritten() {
+        Response res = target().path("test/before").request("test/test").get();
+        assertEquals(200, res.getStatus());
+        assertEquals("exception-before-first-bytes-exception-mapper", res.readEntity(String.class));
+    }
+
+    @Test
+    public void testWriterThrowsExceptionAfterFirstBytesAreWritten() throws IOException {
+        Response res = target().path("test/after").request("test/test").get();
+        assertEquals(200, res.getStatus());
+        final InputStream inputStream = res.readEntity(InputStream.class);
+        byte b;
+        inputStream.read();
+        MyMessageBodyWritter.firstBytesReceived = true;
+        while ((b = (byte) inputStream.read()) >= 0) {
+            assertEquals('a', b);
+        }
+    }
+
+    @Test
+    public void testPreventMultipleExceptionMapping() {
+        Response res = target().path("test/exception").request("test/test").get();
+        // firstly exception is thrown in the resource method and is correctly mapped. Then it is again thrown in MBWriter but
+        // exception can be mapped only once, so second exception in MBWriter cause 500 response code.
+        assertEquals(500, res.getStatus());
+    }
+
+    @Path("test")
+    public static class Resource {
+
+        @GET
+        @Path("before")
+        @Produces("test/test")
+        public Response exceptionBeforeFirstBytesAreWritten() {
+            return Response.status(200).header("writer-exception", "before-first-byte").entity("ok").build();
+        }
+
+        @GET
+        @Path("after")
+        @Produces("test/test")
+        public Response exceptionAfterFirstBytesAreWritten() {
+            return Response.status(200).header("writer-exception", "after-first-byte").entity("aaaaa").build();
+        }
+
+        @POST
+        @Produces("test/test")
+        public String post(String str) {
+            return "post";
+        }
+
+        @GET
+        @Path("exception")
+        @Produces("test/test")
+        public Response throwsException() {
+            throw new MyAnotherException("resource");
+        }
+
+        @Path("throwable")
+        @GET
+        public String throwsThrowable() throws Throwable {
+            throw new Throwable("throwable",
+                    new RuntimeException("runtime-exception",
+                            new ClientErrorException("client-error", 499)));
+        }
+    }
+
+    public static class ClientErrorExceptionMapper implements ExceptionMapper<ClientErrorException> {
+
+        @Override
+        public Response toResponse(ClientErrorException exception) {
+            return Response.status(Response.Status.OK).entity("mapped-client-error-"
+                    + exception.getResponse().getStatus() + "-" + exception.getMessage()).build();
+        }
+    }
+
+    public static class MyExceptionMapper implements ExceptionMapper<MyException> {
+
+        @Override
+        public Response toResponse(MyException exception) {
+            return Response.ok().entity(exception.getMessage() + "-exception-mapper").build();
+        }
+    }
+
+    public static class ThrowableMapper implements ExceptionMapper<Throwable> {
+
+        @Override
+        public Response toResponse(Throwable throwable) {
+            throwable.printStackTrace();
+            return Response.status(Response.Status.OK).entity("mapped-throwable-" + throwable.getMessage()).build();
+        }
+
+    }
+
+    public static class MyExceptionMapperCauseAnotherException implements ExceptionMapper<MyAnotherException> {
+
+        @Override
+        public Response toResponse(MyAnotherException exception) {
+            // the header causes exception to be thrown again in MyMessageBodyWriter
+            return Response.ok().header("writer-exception", "before-first-byte").entity(exception.getMessage()
+                    + "-another-exception-mapper").build();
+        }
+    }
+
+    @Produces("test/test")
+    public static class MyMessageBodyWritter implements MessageBodyWriter<String> {
+
+        public static volatile boolean firstBytesReceived;
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return s.length();
+        }
+
+        @Override
+        public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                WebApplicationException {
+            firstBytesReceived = false;
+
+            final List<Object> header = httpHeaders.get("writer-exception");
+
+            if (header != null && header.size() > 0) {
+                if (header.get(0).equals("before-first-byte")) {
+                    throw new MyException("exception-before-first-bytes");
+                } else if (header.get(0).equals("after-first-byte")) {
+                    int i = 0;
+                    while (!firstBytesReceived && i++ < 500000) {
+                        entityStream.write('a');
+                        entityStream.flush();
+                    }
+                    throw new MyException("exception-after-first-bytes");
+                }
+            } else {
+                OutputStreamWriter osw = new OutputStreamWriter(entityStream);
+                osw.write(s);
+                osw.flush();
+            }
+        }
+    }
+
+    @Consumes("test/test")
+    public static class MyMessageBodyReader implements MessageBodyReader<String> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public String readFrom(Class<String> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                               MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException,
+                WebApplicationException {
+            final List<String> header = httpHeaders.get("reader-exception");
+            if (header != null && header.size() > 0 && header.get(0).equals("throw")) {
+                throw new MyException("reader");
+            }
+            return "aaa";
+        }
+    }
+
+    public static class MyException extends RuntimeException {
+
+        public MyException() {
+        }
+
+        public MyException(Throwable cause) {
+            super(cause);
+        }
+
+        public MyException(String message) {
+            super(message);
+        }
+
+        public MyException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    public static class MyAnotherException extends RuntimeException {
+
+        public MyAnotherException() {
+        }
+
+        public MyAnotherException(Throwable cause) {
+            super(cause);
+        }
+
+        public MyAnotherException(String message) {
+            super(message);
+        }
+
+        public MyAnotherException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * BEGIN: JERSEY-1515 reproducer code
+     */
+    public static class VisibilityException extends WebApplicationException {
+
+        private static final long serialVersionUID = -1159407312691372429L;
+
+    }
+
+    public static class VisibilityExceptionMapper implements ExceptionMapper<VisibilityException> {
+
+        private HttpHeaders headers;
+        private UriInfo info;
+        private Application application;
+        private Request request;
+        private Providers provider;
+
+        protected VisibilityExceptionMapper(@Context HttpHeaders headers,
+                                            @Context UriInfo info, @Context Application application,
+                                            @Context Request request, @Context Providers provider) {
+            super();
+            this.headers = headers;
+            this.info = info;
+            this.application = application;
+            this.request = request;
+            this.provider = provider;
+        }
+
+        public VisibilityExceptionMapper(@Context HttpHeaders headers,
+                                         @Context UriInfo info, @Context Application application,
+                                         @Context Request request) {
+            super();
+            this.headers = headers;
+            this.info = info;
+            this.application = application;
+            this.request = request;
+        }
+
+        public VisibilityExceptionMapper(@Context HttpHeaders headers,
+                                         @Context UriInfo info, @Context Application application) {
+            super();
+            this.headers = headers;
+            this.info = info;
+            this.application = application;
+        }
+
+        public VisibilityExceptionMapper(@Context HttpHeaders headers,
+                                         @Context UriInfo info) {
+            super();
+            this.headers = headers;
+            this.info = info;
+        }
+
+        public VisibilityExceptionMapper(@Context HttpHeaders headers) {
+            super();
+            this.headers = headers;
+        }
+
+        @Override
+        public Response toResponse(VisibilityException exception) {
+            return Response.ok("visible").build();
+        }
+    }
+
+    @Path("test/visible")
+    public static class TestResource {
+
+        @GET
+        public String throwVisibleException() {
+            throw new VisibilityException();
+        }
+    }
+
+    @Test
+    public void testJersey1515() {
+        Response res = target().path("test/visible").request().get();
+        assertEquals(200, res.getStatus());
+        assertEquals("visible", res.readEntity(String.class));
+    }
+    /**
+     * END: JERSEY-1515 reproducer code
+     */
+
+    /**
+     * BEGIN: JERSEY-1525 reproducer code.
+     */
+    @Path("test/responseFilter")
+    public static class ExceptionTestResource {
+
+        @GET
+        @ThrowsNPE
+        public String getData() {
+            return "method";
+        }
+    }
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface ThrowsNPE {
+    }
+
+    @ThrowsNPE
+    public static class ExceptionThrowingFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext,
+                           ContainerResponseContext responseContext) throws IOException {
+            // The if clause prevents throwing exception on a mapped response.
+            // Not doing so would result in a second exception being thrown
+            // which would not be mapped again; instead, it would be propagated
+            // to the hosting container directly.
+            if (!"mapped-throwable-response-filter-exception".equals(responseContext.getEntity())) {
+                throw new NullPointerException("response-filter-exception");
+            }
+        }
+    }
+
+    @Test
+    public void testJersey1525() {
+        Response res = target().path("test/responseFilter").request().get();
+        assertEquals(200, res.getStatus());
+        assertEquals("mapped-throwable-response-filter-exception", res.readEntity(String.class));
+    }
+
+    /**
+     * END: JERSEY-1525 reproducer code
+     */
+
+    @Provider
+    public static class IOExceptionMessageReader implements MessageBodyReader<IOBean>, MessageBodyWriter<IOBean> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == IOBean.class;
+        }
+
+        @Override
+        public IOBean readFrom(Class<IOBean> type,
+                               Type genericType, Annotation[] annotations, MediaType mediaType,
+                               MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            throw new IOException("io-exception");
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == IOBean.class;
+        }
+
+        @Override
+        public long getSize(IOBean ioBean, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return 0;
+        }
+
+        @Override
+        public void writeTo(IOBean ioBean, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                WebApplicationException {
+            entityStream.write(ioBean.value.getBytes());
+
+        }
+    }
+
+    public static class IOBean {
+
+        private final String value;
+
+        public IOBean(String value) {
+            this.value = value;
+        }
+    }
+
+    public static class IOExceptionMapper implements ExceptionMapper<IOException> {
+
+        @Override
+        public Response toResponse(IOException exception) {
+            return Response.ok("passed").build();
+        }
+    }
+
+    @Path("io")
+    public static class IOExceptionResource {
+
+        @POST
+        public String post(IOBean iobean) {
+            return iobean.value;
+        }
+    }
+
+    @Test
+    public void testIOException() {
+        final Response response = target().register(IOExceptionMessageReader.class)
+                .path("io").request().post(Entity.entity(new IOBean("io-bean"), MediaType.TEXT_PLAIN));
+        assertEquals(200, response.getStatus());
+        assertEquals("passed", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testThrowableFromResourceMethod() {
+        Response res = target().path("test/throwable").request().get();
+        assertEquals(200, res.getStatus());
+        assertEquals("mapped-throwable-throwable", res.readEntity(String.class));
+    }
+
+    @Path("not-found")
+    public static class MessageBodyProviderNotFoundResource {
+
+        @GET
+        @Produces("aa/bbb")
+        public UnknownType get() {
+            return new UnknownType();
+        }
+    }
+
+    public static class UnknownType {
+
+    }
+
+    public static class ProviderNotFoundExceptionMapper implements ExceptionMapper<InternalServerErrorException> {
+
+        @Override
+        public Response toResponse(InternalServerErrorException exception) {
+            if (exception.getCause() instanceof MessageBodyProviderNotFoundException) {
+                return Response.ok("mapped-by-ProviderNotFoundExceptionMapper").build();
+            }
+            return Response.serverError().entity("Unexpected root cause of InternalServerError").build();
+        }
+    }
+
+    /**
+     * Tests that {@link MessageBodyProviderNotFoundException} wrapped into {@link javax.ws.rs.InternalServerErrorException}
+     * is correctly mapped using an {@link ExceptionMapper}.
+     */
+    @Test
+    public void testNotFoundResource() {
+        final Response response = target().path("not-found").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("mapped-by-ProviderNotFoundExceptionMapper", response.readEntity(String.class));
+    }
+
+    public static class Jersey1887Exception extends RuntimeException {
+    }
+
+    @Provider
+    public static interface Jersey1887ExceptionMapper extends ExceptionMapper<Jersey1887Exception> {
+    }
+
+    public static class Jersey1887ExceptionMapperImpl implements Jersey1887ExceptionMapper {
+
+        @Override
+        public Response toResponse(final Jersey1887Exception exception) {
+            return Response.ok("found").build();
+        }
+    }
+
+    @Path("jersey1887")
+    public static class Jersey1887Resource {
+
+        @GET
+        public Response get() {
+            throw new Jersey1887Exception();
+        }
+    }
+
+    /**
+     * Test that we're able to use correct exception mapper even when the mapper hierarchy has complex inheritance.
+     */
+    @Test
+    public void testJersey1887() throws Exception {
+        final Response response = target().path("jersey1887").request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("found"));
+    }
+
+    public static class Jersey2382Exception extends RuntimeException {
+    }
+
+    public static class Jersey2382Entity {
+    }
+
+    @Provider
+    public static class Jersey2382Provider implements MessageBodyWriter<Jersey2382Entity> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(final Jersey2382Entity jersey2382Entity, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final Jersey2382Entity jersey2382Entity,
+                            final Class<?> type,
+                            final Type genericType,
+                            final Annotation[] annotations,
+                            final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            if (Jersey2382Entity.class != type) {
+                entityStream.write("wrong-type".getBytes());
+            } else if (Jersey2382Entity.class != genericType) {
+                entityStream.write("wrong-generic-type".getBytes());
+            } else {
+                entityStream.write("ok".getBytes());
+            }
+        }
+    }
+
+    @Provider
+    public static class Jersey2382ExceptionMapper implements ExceptionMapper<Jersey2382Exception> {
+
+        @Override
+        public Response toResponse(final Jersey2382Exception exception) {
+            return Response.ok(new Jersey2382Entity()).build();
+        }
+    }
+
+    @Path("jersey2382")
+    public static class Jersey2382Resource {
+
+        @GET
+        public List<List<Integer>> get() {
+            throw new Jersey2382Exception();
+        }
+    }
+
+    /**
+     * Test that we're able to use correct exception mapper even when the mapper hierarchy has complex inheritance.
+     */
+    @Test
+    public void testJersey2382() throws Exception {
+        final Response response = target().path("jersey2382").request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("ok"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java
new file mode 100644
index 0000000..350b8c4
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2013, 2018 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.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.internal.LocalizationMessages;
+import org.glassfish.jersey.spi.ExtendedExceptionMapper;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Miroslav Fuksa
+ *
+ */
+public class ExtendedExceptionMapperTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
+
+        return new ResourceConfig(
+                TestResource.class,
+                ExceptionMapperA.class,
+                ExceptionMapperB.class,
+                ExceptionMapperC.class,
+                ExceptionMapperD.class,
+                ExceptionMapperE.class,
+                ExceptionMapperX.class
+        );
+    }
+
+    public static class ExceptionA extends RuntimeException {
+        public ExceptionA(String message) {
+            super(message);
+        }
+    }
+
+    public static class ExceptionB extends ExceptionA {
+        public ExceptionB(String message) {
+            super(message);
+        }
+    }
+
+    public static class ExceptionC extends ExceptionB {
+        public ExceptionC(String message) {
+            super(message);
+        }
+    }
+
+    public static class ExceptionD extends ExceptionC {
+        public ExceptionD(String message) {
+            super(message);
+        }
+    }
+
+    public static class ExceptionE extends ExceptionD {
+        public ExceptionE(String message) {
+            super(message);
+        }
+    }
+
+    public static class ExceptionX extends RuntimeException {
+        public ExceptionX(String message) {
+            super(message);
+        }
+    }
+
+
+    public static class ExceptionMapperA implements ExtendedExceptionMapper<ExceptionA> {
+
+        @Override
+        public boolean isMappable(ExceptionA exception) {
+            return exception.getMessage().substring(2).contains("A");
+        }
+
+        @Override
+        public Response toResponse(ExceptionA exception) {
+            return Response.ok("A").build();
+        }
+    }
+
+    public static class ExceptionMapperB implements ExtendedExceptionMapper<ExceptionB> {
+
+        @Override
+        public boolean isMappable(ExceptionB exception) {
+            return exception.getMessage().substring(2).contains("B");
+        }
+
+        @Override
+        public Response toResponse(ExceptionB exception) {
+            return Response.ok("B").build();
+        }
+    }
+
+    public static class ExceptionMapperC implements ExceptionMapper<ExceptionC> {
+
+        @Override
+        public Response toResponse(ExceptionC exception) {
+            return Response.ok("C").build();
+        }
+    }
+
+    public static class ExceptionMapperD implements ExtendedExceptionMapper<ExceptionD> {
+
+        @Override
+        public boolean isMappable(ExceptionD exception) {
+            return exception.getMessage().substring(2).contains("D");
+        }
+
+        @Override
+        public Response toResponse(ExceptionD exception) {
+            return Response.ok("D").build();
+        }
+    }
+
+    public static class ExceptionMapperE implements ExtendedExceptionMapper<ExceptionE> {
+
+        @Override
+        public boolean isMappable(ExceptionE exception) {
+            return exception.getMessage().substring(2).contains("E");
+        }
+
+        @Override
+        public Response toResponse(ExceptionE exception) {
+            return Response.ok("E").build();
+        }
+    }
+
+    public static class ExceptionMapperX implements ExtendedExceptionMapper<ExceptionX> {
+
+        @Override
+        public boolean isMappable(ExceptionX exception) {
+            return exception.getMessage().substring(2).contains("X");
+        }
+
+        @Override
+        public Response toResponse(ExceptionX exception) {
+            return Response.ok("X").build();
+        }
+    }
+
+    @Path("resource")
+    public static class TestResource {
+        @POST
+        public String post(String e) {
+            if (e.charAt(0) == 'A') {
+                throw new ExceptionA(String.valueOf(e));
+            } else if (e.charAt(0) == 'B') {
+                throw new ExceptionB(String.valueOf(e));
+            } else if (e.charAt(0) == 'C') {
+                throw new ExceptionC(String.valueOf(e));
+            } else if (e.charAt(0) == 'D') {
+                throw new ExceptionD(String.valueOf(e));
+            } else if (e.charAt(0) == 'E') {
+                throw new ExceptionE(String.valueOf(e));
+            } else if (e.charAt(0) == 'X') {
+                throw new ExceptionX(String.valueOf(e));
+            }
+            return "get";
+        }
+    }
+
+    @Test
+    public void test() {
+        // Format of first param: [exception thrown]-[exception mappers which will return true in isMappable]
+        _test("A-A", "A");
+        _test("A-AB", "A");
+        _test("A-ABC", "A");
+        _test("A-ABCD", "A");
+        _test("A-ABCDE", "A");
+        _test("A-ABCDEX", "A");
+        _test("A-C", null);
+        _test("A-D", null);
+        _test("A-E", null);
+        _test("A-D", null);
+        _test("A-BCDEX", null);
+        _test("A-00000", null);
+        _test("A-X", null);
+
+
+        _test("B-A", "A");
+        _test("B-B", "B");
+        _test("B-AB", "B");
+        _test("B-ABC", "B");
+        _test("B-ABCD", "B");
+        _test("B-ABCDE", "B");
+        _test("B-ABCDEX", "B");
+        _test("B-C", null);
+        _test("B-D", null);
+        _test("B-E", null);
+        _test("B-CDEX", null);
+        _test("B-X", null);
+        _test("B-000", null);
+
+        // C is not an ExtendedExceptionMapper but just ExceptionMapper (always mappable)
+        _test("C-C", "C");
+        _test("C-A", "C");
+        _test("C-AB", "C");
+        _test("C-ABC", "C");
+        _test("C-AEX", "C");
+        _test("C-00000", "C");
+        _test("C-ABCDEX", "C");
+        _test("C-E", "C");
+        _test("C-DE", "C");
+        _test("C-D", "C");
+        _test("C-X", "C");
+
+        _test("D-000", "C");
+        _test("D-A", "C");
+        _test("D-B", "C");
+        _test("D-C", "C");
+        _test("D-D", "D");
+        _test("D-E", "C");
+        _test("D-ABC", "C");
+        _test("D-ABCEX", "C");
+        _test("D-ABCDEX", "D");
+        _test("D-DE", "D");
+        _test("D-ABEX", "C");
+        _test("D-AEX", "C");
+        _test("D-X", "C");
+
+        _test("E-A", "C");
+        _test("E-B", "C");
+        _test("E-C", "C");
+        _test("E-D", "D");
+        _test("E-E", "E");
+        _test("E-ABC", "C");
+        _test("E-ABCD", "D");
+        _test("E-ABCDE", "E");
+        _test("E-ABCDEX", "E");
+        _test("E-DE", "E");
+        _test("E-X", "C");
+        _test("E-000000", "C");
+
+        _test("X-A", null);
+        _test("X-ABCDE", null);
+        _test("X-ABCDEX", "X");
+        _test("X-X", "X");
+
+        // Check logs. (??)
+        for (final LogRecord logRecord : getLoggedRecords()) {
+
+            // TODO: this test is fragile.
+            if (logRecord.getLoggerName().contains("ClientExecutorProvidersConfigurator")) {
+                continue;
+            }
+
+            for (final String message : new String[]{
+                    LocalizationMessages.ERROR_EXCEPTION_MAPPING_ORIGINAL_EXCEPTION(),
+                    LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER()}) {
+
+                if (logRecord.getMessage().contains(message) && logRecord.getLevel().intValue() > Level.FINE.intValue()) {
+                    fail("Log message should be logged at lower (FINE) level: " + message);
+                }
+            }
+        }
+    }
+
+    private void _test(String input, String expectedMapper) {
+        final Response response = target("resource").request().post(Entity.entity(input, MediaType.TEXT_PLAIN_TYPE));
+        if (expectedMapper == null) {
+            Assert.assertEquals(500, response.getStatus());
+        } else {
+            Assert.assertEquals(200, response.getStatus());
+            Assert.assertEquals(expectedMapper, response.readEntity(String.class));
+        }
+    }
+
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedUriInfoTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedUriInfoTest.java
new file mode 100644
index 0000000..b88f94e
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedUriInfoTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015, 2018 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.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Objects;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.server.ExtendedUriInfo;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * {@link ExtendedUriInfo} e2e tests - testing e.g. getting matched resources, mapped throwable, etc.
+ *
+ * @author Michal Gajdos
+ */
+public class ExtendedUriInfoTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ThrowableResource.class, MappedThrowableResponseFilter.class, MappedExceptionMapper.class);
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface MappedThrowable {}
+
+    @Path("mapped-throwable-test")
+    @MappedThrowable
+    public static class ThrowableResource {
+
+        @GET
+        @Path("unmapped")
+        public Response unmapped() {
+            throw new RuntimeException();
+        }
+
+        @GET
+        @Path("mapped")
+        public Response mapped() {
+            throw new MappedException();
+        }
+
+        @GET
+        @Path("webapp")
+        public Response webapp() {
+            throw new InternalServerErrorException();
+        }
+
+        @GET
+        @Path("regular")
+        public Response regular() {
+            return Response.ok().build();
+        }
+    }
+
+    public static class MappedException extends RuntimeException {
+    }
+
+    public static class MappedExceptionMapper implements ExceptionMapper<MappedException> {
+
+        @Override
+        public Response toResponse(final MappedException exception) {
+            return Response.ok().build();
+        }
+    }
+
+    @MappedThrowable
+    public static class MappedThrowableResponseFilter implements ContainerResponseFilter {
+
+        @Inject
+        private ExtendedUriInfo uriInfo;
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
+                throws IOException {
+            responseContext.setEntity(Objects.toString(uriInfo.getMappedThrowable()));
+        }
+    }
+
+    @Test
+    public void testUnmappedThrowableValue() throws Exception {
+        assertThat("Internal Server Error expected - response filter not invoked",
+                target("mapped-throwable-test/unmapped").request().get().getStatus(),
+                is(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()));
+    }
+
+    @Test
+    public void testMappedThrowableValue() throws Exception {
+        assertThat("MappedException expected in ExtendedUriInfo#getMappedThrowable",
+                target("mapped-throwable-test/mapped").request().get().readEntity(String.class),
+                is("org.glassfish.jersey.tests.e2e.server.ExtendedUriInfoTest$MappedException"));
+    }
+
+    @Test
+    public void testWebAppThrowableValue() throws Exception {
+        assertThat("InternalServerErrorException expected in ExtendedUriInfo#getMappedThrowable",
+                target("mapped-throwable-test/webapp").request().get().readEntity(String.class),
+                containsString("javax.ws.rs.InternalServerErrorException"));
+    }
+
+    @Test
+    public void testRegularResourceValue() throws Exception {
+        assertThat("null expected in ExtendedUriInfo#getMappedThrowable",
+                target("mapped-throwable-test/regular").request().get().readEntity(String.class),
+                is("null"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/FormParamMultivaluedInjectionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/FormParamMultivaluedInjectionTest.java
new file mode 100644
index 0000000..8270802
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/FormParamMultivaluedInjectionTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2015, 2018 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 javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that the MultivaluedMap injection does not close the request buffer and allows
+ * to proceed other FormParam injections.
+ *
+ * @author Petr Bouda
+ */
+public class FormParamMultivaluedInjectionTest extends JerseyTest {
+
+    public static final String PREDEFINED_RESPONSE = "Hello George Javatar";
+
+    @Path("form")
+    public static class FormResource {
+
+        @POST
+        @Path("simple")
+        public Response simple(MultivaluedMap<String, String> formParams,
+                              @FormParam("firstname") String firstname,
+                              @FormParam("lastname") String lastname) {
+            assertEquals(2, formParams.size());
+            assertEquals("George", formParams.get("firstname").get(0));
+            assertEquals("Javatar", formParams.get("lastname").get(0));
+            return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
+        }
+
+        @POST
+        @Path("nullable")
+        public Response nullable(MultivaluedMap<String, String> formParams,
+                              @FormParam("firstname") String firstname,
+                              @FormParam("lastname") String lastname) {
+            assertEquals(2, formParams.size());
+            assertEquals(2, formParams.get("firstname").size());
+            assertEquals("George", formParams.get("firstname").get(0));
+            assertEquals("Javatar", formParams.get("lastname").get(0));
+            return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
+        }
+
+        @POST
+        @Path("mixed")
+        public Response mixed(@FormParam("firstname") String firstname,
+                              MultivaluedMap<String, String> formParams,
+                              @FormParam("lastname") String lastname) {
+            assertEquals(2, formParams.size());
+            assertEquals(2, formParams.get("firstname").size());
+            assertEquals("George", formParams.get("firstname").get(0));
+            assertEquals("Javatar", formParams.get("lastname").get(0));
+            return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
+        }
+
+        @POST
+        @Path("encoded")
+        public Response encoded(MultivaluedMap<String, String> formParams,
+                               @Encoded @FormParam("firstname") String firstname,
+                               @FormParam("lastname") String lastname) {
+            assertEquals(2, formParams.size());
+            assertEquals("George", formParams.get("firstname").get(0));
+            assertEquals("Javatar", formParams.get("lastname").get(0));
+            return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
+        }
+
+    }
+
+    @Path("form-ext")
+    public static class FormExtResource {
+
+        @Encoded @FormParam("firstname") String firstname;
+
+        @POST
+        @Path("encoded")
+        public Response encoded(MultivaluedMap<String, String> formParams,
+                               @FormParam("lastname") String lastname) {
+            assertEquals(2, formParams.size());
+            assertEquals("George", formParams.get("firstname").get(0));
+            assertEquals("Javatar", formParams.get("lastname").get(0));
+            return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
+        }
+
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(FormResource.class, FormExtResource.class);
+    }
+
+    @Test
+    public void testFormMultivaluedParam() {
+        Response result = call("/form/simple", "firstname=George&lastname=Javatar");
+        assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testFormMultivaluedParamWithNull() {
+        Response result = call("/form/nullable", "firstname=George&firstname&lastname=Javatar");
+        assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testFormMultivaluedParamMixedParamOrder() {
+        Response result = call("/form/mixed", "firstname=George&firstname&lastname=Javatar");
+        assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testFormMultivaluedParamEncoded() {
+        Response result = call("/form/encoded", "firstname=George&lastname=Javatar");
+        assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testFormMultivaluedParamExternalEncodedInjection() {
+        Response result = call("/form-ext/encoded", "firstname=George&lastname=Javatar");
+        assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
+    }
+
+    private Response call(String path, String entity) {
+        return target().path(path).request()
+                .post(Entity.entity(entity, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+    }
+}
+
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GlobalNameBoundInterceptorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GlobalNameBoundInterceptorTest.java
new file mode 100644
index 0000000..5aaec35
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GlobalNameBoundInterceptorTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests name bound and global bound interceptors.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class GlobalNameBoundInterceptorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                InterceptorTestBound.class,
+                InterceptorGlobal.class,
+                PreMatchFilter.class,
+                TestResource.class,
+                TestExceptionMapper.class,
+                RequestFilter.class,
+                ReaderInterceptorGlobal.class,
+                ReaderInterceptorPostBound.class);
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface TestBound {}
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface PostBound {}
+
+    @Path("resource")
+    public static class TestResource {
+        @GET
+        @TestBound
+        public String get() {
+            return "get";
+        }
+
+        @GET
+        @Path("standard")
+        public String getStandard() {
+            return "ok";
+        }
+
+        @POST
+        @PostBound
+        @Path("postBound")
+        public String postBound(String str) {
+            return str;
+        }
+
+        @POST
+        @Path("postGlobal")
+        public String postGlobal(String str) {
+            return str;
+        }
+    }
+
+    public static class TestException extends RuntimeException {
+        public TestException(String message) {
+            super(message);
+        }
+    }
+
+    public static class TestExceptionMapper implements ExceptionMapper<TestException> {
+
+        @Override
+        public Response toResponse(TestException exception) {
+            return Response.ok("mapped-" + exception.getMessage()).build();
+        }
+    }
+
+
+    @PreMatching
+    public static class PreMatchFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if ("exception".equals(requestContext.getHeaderString("pre-filter"))) {
+                throw new TestException("(pre-matching-exception)");
+            } else if ("abort".equals(requestContext.getHeaderString("pre-filter"))) {
+                requestContext.abortWith(Response.ok("(pre-matching-abort)").build());
+            }
+        }
+    }
+
+    public static class RequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if ("exception".equals(requestContext.getHeaderString("request-filter"))) {
+                throw new TestException("(request-filter-exception)");
+            } else if ("abort".equals(requestContext.getHeaderString("request-filter"))) {
+                requestContext.abortWith(Response.ok("(request-filter-abort)").build());
+            }
+        }
+    }
+
+    @TestBound
+    @Priority(200)
+    public static class InterceptorTestBound implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context)
+                throws IOException, WebApplicationException {
+            String entity = context.getEntity() + "-[test-bound]";
+            context.setEntity(entity);
+            context.proceed(); //Add one
+        }
+    }
+
+    @Priority(100)
+    public static class InterceptorGlobal implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context)
+                throws IOException, WebApplicationException {
+            String entity = context.getEntity() + "-[global-bound]";
+            context.setEntity(entity);
+            context.proceed(); //Add one
+        }
+    }
+
+    @Priority(200)
+    public static class ReaderInterceptorGlobal implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final String entity = (String) context.proceed();
+            return entity + "-[global-reader-interceptor]";
+        }
+    }
+
+    @Priority(100)
+    @PostBound
+    public static class ReaderInterceptorPostBound implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final String entity = (String) context.proceed();
+            return entity + "-[post-reader-interceptor]";
+        }
+    }
+
+    @Test
+    public void testPrematchingException() {
+        final Response response = target("resource").request().header("pre-filter", "exception").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("mapped-(pre-matching-exception)-[global-bound]", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPrematchingAbort() {
+        final Response response = target("resource").request().header("pre-filter", "abort").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("(pre-matching-abort)-[global-bound]", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testRequestFilterException() {
+        final Response response = target("resource").request().header("request-filter", "exception").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("mapped-(request-filter-exception)-[global-bound]-[test-bound]", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testRequestFilterAbort() {
+        final Response response = target("resource").request().header("request-filter", "abort").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("(request-filter-abort)-[global-bound]-[test-bound]", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testStandardResource() {
+        final Response response = target("resource/standard").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("ok-[global-bound]", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testTestResource() {
+        final Response response = target("resource").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("get-[global-bound]-[test-bound]", response.readEntity(String.class));
+    }
+
+
+    @Test
+    public void testPost() {
+        final Response response = target("resource/postGlobal").request().post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("post-[global-reader-interceptor]-[global-bound]", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPostBound() {
+        final Response response = target("resource/postBound").request().post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("post-[global-reader-interceptor]-[post-reader-interceptor]-[global-bound]",
+                response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GloballyNameBoundResourceFilterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GloballyNameBoundResourceFilterTest.java
new file mode 100644
index 0000000..da48b79
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GloballyNameBoundResourceFilterTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * JAX-RS global name-bound filter tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @see ResourceFilterTest
+ */
+public class GloballyNameBoundResourceFilterTest extends JerseyTest {
+
+    public static final String TEST_REQUEST_HEADER = "test-request-header";
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @GloballyBound
+    public static final class MyApplication extends ResourceConfig {
+        public MyApplication() {
+            super(
+                    MyResource.class,
+                    GloballyBoundRequestFilter.class,
+                    GloballyBoundResponseFilter.class
+
+            );
+        }
+    }
+
+    @Test
+    public void testGlobalyBoundPostMatching() {
+        Response r = target("postMatching").request().get();
+        assertThat(r.getStatus(), equalTo(200));
+        assertThat(r.hasEntity(), is(true));
+        assertThat(r.readEntity(String.class), equalTo("requestFilter-method-responseFilter"));
+    }
+
+    // See JERSEY-1554
+    @Test
+    public void testGlobalyBoundPostMatchingRequestFilterNotInvokedOn404() {
+        Response r = target("notFound").request().get();
+        assertEquals(404, r.getStatus());
+        assertThat(r.hasEntity(), is(true));
+        assertThat(r.readEntity(String.class), equalTo("responseFilter"));
+    }
+
+    @Path("/")
+    public static class MyResource {
+
+        @Path("postMatching")
+        @GET
+        public String getPostMatching(@Context HttpHeaders headers) {
+            final String header = headers.getHeaderString(TEST_REQUEST_HEADER);
+            return header + "-method";
+        }
+    }
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface GloballyBound {
+    }
+
+    @GloballyBound
+    public static class GloballyBoundRequestFilter implements ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.getHeaders().putSingle(TEST_REQUEST_HEADER, "requestFilter");
+        }
+    }
+
+    @GloballyBound
+    public static class GloballyBoundResponseFilter implements ContainerResponseFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            responseContext.setEntity(
+                    responseContext.hasEntity() ? responseContext.getEntity() + "-responseFilter" : "responseFilter",
+                    responseContext.getEntityAnnotations(),
+                    responseContext.getMediaType());
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GrizzlyInjectionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GrizzlyInjectionTest.java
new file mode 100644
index 0000000..240fe6e
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GrizzlyInjectionTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test for JERSEY-1170
+ *
+ * @author Paul Sandoz
+ */
+public class GrizzlyInjectionTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new GrizzlyTestContainerFactory();
+    }
+
+    @Path("/")
+    public static class Resource {
+        private final Request request;
+
+        public Resource(
+                @Context Request request,
+                @Context Response response) {
+            assertNotNull(request);
+            assertNotNull(response);
+            this.request = request;
+        }
+
+        @GET
+        public String get() {
+            return request.getMethod().getMethodString().toUpperCase();
+        }
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        final String s = target().path("/").request().get(String.class);
+        assertEquals("GET", s);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InitializationLoggingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InitializationLoggingTest.java
new file mode 100644
index 0000000..022ad21
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InitializationLoggingTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, 2018 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.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.internal.LocalizationMessages;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class InitializationLoggingTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
+        return new ResourceConfig(A.class);
+    }
+
+    @Path("test")
+    public static class A {
+
+        @GET
+        public String get() {
+            return "test";
+        }
+    }
+
+    @Test
+    public void test() {
+        final Response r = target().path("test").request().get(Response.class);
+
+        assertTrue(r.readEntity(String.class).contains("test"));
+
+        boolean found = false;
+
+        for (LogRecord logRecord : getLoggedRecords()) {
+            if (logRecord.getMessage().contains(LocalizationMessages.LOGGING_APPLICATION_INITIALIZED())) {
+                found = true;
+                break;
+            }
+        }
+
+        assertTrue(found);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionManagerServerProviderTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionManagerServerProviderTest.java
new file mode 100644
index 0000000..d44521a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionManagerServerProviderTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2014, 2018 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.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.InjectionManagerProvider;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link InjectionManagerProvider}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class InjectionManagerServerProviderTest extends JerseyTest {
+
+    @Path("resource")
+    public static class TestResource {
+        @POST
+        @Path("feature")
+        @FeatureBound
+        public String echoFeature(String entity) {
+            return entity;
+        }
+
+
+        @POST
+        @Path("reader-interceptor")
+        @ReaderInterceptorBound
+        public String echoReaderInterceptor(String entity) {
+            return entity;
+        }
+
+        @POST
+        @Path("writer-interceptor")
+        @WriterInterceptorBound
+        public String echoWriterInterceptor(String entity) {
+            return entity;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(TestResource.class);
+        resourceConfig.register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(new MyInjectedService("hello")).to(MyInjectedService.class);
+            }
+        });
+        resourceConfig.register(new MyFeature());
+        resourceConfig.register(new MyReaderInterceptor());
+        resourceConfig.register(new MyWriterInterceptor());
+        return resourceConfig;
+    }
+
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface FeatureBound {
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface ReaderInterceptorBound {
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface WriterInterceptorBound {
+    }
+
+
+    public static class MyInjectedService {
+        public final String name;
+
+        public MyInjectedService(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+
+    public static class MyFeature implements Feature {
+
+        @Override
+        public boolean configure(FeatureContext context) {
+            context.register(MyFeatureInterceptor.class);
+            return true;
+        }
+
+
+        @FeatureBound
+        public static class MyFeatureInterceptor implements WriterInterceptor {
+            private final String name;
+
+            @Inject
+            public MyFeatureInterceptor(MyInjectedService injectedService) {
+                this.name = injectedService.getName();
+            }
+
+            @Override
+            public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+                context.setEntity(((String) context.getEntity()) + "-interceptorfeature-" + name);
+                context.proceed();
+            }
+        }
+    }
+
+    @Test
+    public void testFeature() {
+        final Response response = target().path("resource/feature")
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-interceptorfeature-hello", response.readEntity(String.class));
+    }
+
+    @WriterInterceptorBound
+    public static class MyWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final InjectionManager serviceLocator = InjectionManagerProvider.getInjectionManager(context);
+            final MyInjectedService service = serviceLocator.getInstance(MyInjectedService.class);
+            context.setEntity(((String) context.getEntity()) + "-writer-interceptor-" + service.getName());
+            context.proceed();
+        }
+    }
+
+
+    @Test
+    public void testWriterInterceptor() {
+        final Response response = target().path("resource/writer-interceptor")
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-writer-interceptor-hello", response.readEntity(String.class));
+    }
+
+
+    @ReaderInterceptorBound
+    public static class MyReaderInterceptor implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final Object entity = context.proceed();
+            if (!(entity instanceof String)) {
+                return entity;
+            }
+            final String stringEntity = (String) entity;
+            final InjectionManager serviceLocator = InjectionManagerProvider.getInjectionManager(context);
+            final MyInjectedService service = serviceLocator.getInstance(MyInjectedService.class);
+            return stringEntity + "-reader-interceptor-" + service.getName();
+        }
+    }
+
+    @Test
+    public void testReaderInterceptorInstance() {
+        final Response response = target().path("resource/reader-interceptor")
+                .request().post(Entity.entity("will-be-extended-by", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("will-be-extended-by-reader-interceptor-hello", response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionTest.java
new file mode 100644
index 0000000..da84650
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013, 2018 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.util.concurrent.Executors;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Injection E2E tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class InjectionTest extends JerseyTest {
+
+    @Path("injection")
+    public static class InjectionTestResource {
+
+        @DELETE
+        @Path("delete-path-param/{id}")
+        public String deletePathParam(String body, @PathParam("id") String id) {
+            return "deleted: " + id + "-" + body;
+        }
+
+        @DELETE
+        @Path("delete-path-param-async/{id}")
+        public void deletePathParam(String body, @PathParam("id") String id, @Suspended AsyncResponse ar) {
+            ar.resume("deleted: " + id + "-" + body);
+        }
+
+        @GET
+        @Path("async")
+        public void asyncGet(@Context final UriInfo uriInfo,
+                             @Context final Request request,
+                             @Context final HttpHeaders headers,
+                             @Context final SecurityContext securityContext,
+                             @Suspended final AsyncResponse response) {
+
+            // now suspend and resume later on with
+            Executors.newSingleThreadExecutor().submit(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        response.resume(String.format("base uri: %s\nheaders: %s\nmethod: %s\nprincipal: %s",
+                                uriInfo.getBaseUriBuilder().build(),
+                                headers.getRequestHeaders(),
+                                request.getMethod(),
+                                securityContext.getUserPrincipal()));
+                    } catch (Throwable e) {
+                        response.resume(e);
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(InjectionTestResource.class);
+    }
+
+    /**
+     * JERSEY-1711 reproducer.
+     *
+     * The test is ignored as it currently fails on the following:
+     * - HttpURLConnection throws a java.net.ProtocolException when trying to send request data with HTTP DELETE
+     * - Grizzly container ignores any DELETE request data and does not pass them to Jersey
+     *
+     * We would need to by-pass these issues in underlying layer to un-ignore the test.
+     */
+    @Test
+    @Ignore
+    public void testInjectionIntoDeleteMethod() {
+        Response response;
+
+        response = target("injection").path("delete-path-param/test").request()
+                .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
+                .method("DELETE", Entity.text("body"));
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 200, response.getStatus());
+        assertEquals("Unexpected response entity.", "deleted: test-body", response.readEntity(String.class));
+
+        response = target("injection").path("delete-path-param-async/test").request()
+                .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
+                .method("DELETE", Entity.text("body"));
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 200, response.getStatus());
+        assertEquals("Unexpected response entity.", "deleted: test-body", response.readEntity(String.class));
+    }
+
+    /**
+     * JERSEY-1761 reproducer.
+     *
+     * This is to make sure no proxy gets injected into async method parameters.
+     */
+    @Test
+    public void testAsyncMethodParamInjection() {
+
+        Response response = target("injection").path("async").request().get();
+        assertEquals("Unexpected response status.", 200, response.getStatus());
+        assertNotNull("Response is null.", response);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InputStreamResponseTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InputStreamResponseTest.java
new file mode 100644
index 0000000..feef6b4
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InputStreamResponseTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This is to make sure you can just pass an input stream to Jersey,
+ * where entity body data would be read from.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InputStreamResponseTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(InputStreamResource.class, WrappedInputStreamResource.class);
+    }
+
+    @Path("/directInputStream")
+    public static class InputStreamResource {
+
+        @GET
+        public InputStream get() {
+            return new ByteArrayInputStream("Josefka".getBytes());
+        }
+    }
+
+    @Test
+    public void testDirectInputStream() throws Exception {
+        final String s = target().path("directInputStream").request().get(String.class);
+
+        assertEquals("Josefka", s);
+    }
+
+    @Path("/responseWrappedInputStream")
+    public static class WrappedInputStreamResource {
+
+        @GET
+        public Response get() {
+            return Response.ok(new ByteArrayInputStream("Marie".getBytes()), MediaType.TEXT_PLAIN_TYPE).build();
+        }
+    }
+
+    @Test
+    public void testWrappedInputStream() throws Exception {
+        final Response response = target().path("responseWrappedInputStream").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+        assertEquals("Marie", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorHttpHeadersInjectionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorHttpHeadersInjectionTest.java
new file mode 100644
index 0000000..12be8fc
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorHttpHeadersInjectionTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for JERSEY-1545.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InterceptorHttpHeadersInjectionTest extends JerseyTest {
+
+    static final String WriterHEADER = "custom-writer-header";
+    static final String ReaderHEADER = "custom-reader-header";
+    static final String RawCONTENT = "SIMPLE";
+
+    @Provider
+    public static class InjectedWriterInterceptor implements WriterInterceptor {
+
+        @Context
+        HttpHeaders headers;
+
+        // Replace content with WriterHEADER header value if corresponding header is seen.
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final String writerHeaderValue = headers.getHeaderString(WriterHEADER);
+
+            if (writerHeaderValue != null) {
+                context.getOutputStream().write(writerHeaderValue.getBytes());
+            }
+
+            context.proceed();
+        }
+    }
+
+    @Provider
+    public static class InjectedReaderInterceptor implements ReaderInterceptor {
+
+        @Context
+        HttpHeaders headers;
+
+        // Replace content with ReaderHEADER header value if corresponding header is seen.
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final String readerHeaderValue = headers.getHeaderString(ReaderHEADER);
+
+            if (readerHeaderValue != null) {
+                return readerHeaderValue;
+            }
+            return context.proceed();
+        }
+    }
+
+    @Path("/")
+    public static class SimpleResource {
+
+        @GET
+        public String getIt() {
+            return RawCONTENT;
+        }
+
+        @POST
+        public String echo(String message) {
+            return message;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(SimpleResource.class, InjectedWriterInterceptor.class, InjectedReaderInterceptor.class);
+    }
+
+    // No interceptor should tweak the content if there is not header present.
+    private void _checkRawGet() {
+        final String result = target().request().get(String.class);
+        assertThat(result, containsString(RawCONTENT));
+    }
+
+    @Test
+    public void testWriter() throws Exception {
+        _checkRawGet();
+        _checkWriterInterceptor("writer-one");
+        _checkWriterInterceptor("writer-two");
+    }
+
+    // set WriterHEADER header and check the same value is returned back
+    private void _checkWriterInterceptor(final String headerValue) {
+        final String result = target().request().header(WriterHEADER, headerValue).get(String.class);
+        assertThat(result, containsString(headerValue));
+    }
+
+    @Test
+    public void testReader() throws Exception {
+        _checkRawEcho();
+        _checkReaderInterceptor("reader-one");
+        _checkReaderInterceptor("reader-two");
+    }
+
+    // No interceptor should tweak the content if there is not header present.
+    private void _checkRawEcho() {
+        final String rawResult = target().request().post(Entity.text(RawCONTENT), String.class);
+        assertThat(rawResult, containsString(RawCONTENT));
+    }
+
+    // set ReaderHEADER header and check the same value is returned back
+    private void _checkReaderInterceptor(String headerValue) {
+        final String result = target().request().header(ReaderHEADER, headerValue).post(Entity.text(RawCONTENT), String.class);
+        assertThat(result, containsString(headerValue));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorNameAndDynamicBindingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorNameAndDynamicBindingTest.java
new file mode 100644
index 0000000..d9942c7
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorNameAndDynamicBindingTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InterceptorNameAndDynamicBindingTest extends JerseyTest {
+
+    static final String ENTITY = "ENTITY";
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        super.configureClient(config);
+    }
+
+    abstract static class PrefixAddingReaderInterceptor implements ReaderInterceptor {
+
+        public PrefixAddingReaderInterceptor() {
+        }
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            context.setInputStream(
+                    new SequenceInputStream(new ByteArrayInputStream(getPrefix().getBytes()), context.getInputStream()));
+            return context.proceed();
+        }
+
+        abstract String getPrefix();
+    }
+
+    abstract static class PrefixAddingWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            context.getOutputStream().write(getPrefix().getBytes());
+            context.proceed();
+        }
+
+        abstract String getPrefix();
+    }
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    static @interface NameBoundReader {
+    }
+
+    @NameBoundReader
+    @Priority(40)
+    static class NameBoundReaderInterceptor extends PrefixAddingReaderInterceptor {
+
+        @Override
+        String getPrefix() {
+            return "nameBoundReader";
+        }
+    }
+
+    @Priority(60)
+    static class DynamicallyBoundReaderInterceptor extends PrefixAddingReaderInterceptor {
+
+        @Override
+        String getPrefix() {
+            return "dynamicallyBoundReader";
+        }
+    }
+
+    @NameBinding
+    @Priority(40)
+    @Retention(RetentionPolicy.RUNTIME)
+    static @interface NameBoundWriter {
+    }
+
+    @NameBoundWriter
+    public static class NameBoundWriterInterceptor extends PrefixAddingWriterInterceptor {
+
+        @Override
+        String getPrefix() {
+            return "nameBoundWriter";
+        }
+    }
+
+    @Priority(20)
+    public static class DynamicallyBoundWriterInterceptor extends PrefixAddingWriterInterceptor {
+
+        @Override
+        String getPrefix() {
+            return "dynamicallyBoundWriter";
+        }
+    }
+
+    @Path("method")
+    public static class MethodBindingResource {
+
+        @Path("dynamicallyBoundWriter")
+        @GET
+        public String getDynamicallyBoundWriter() {
+            return ENTITY;
+        }
+
+        @Path("nameBoundWriter")
+        @GET
+        @NameBoundWriter
+        public String getNameBoundWriter() {
+            return ENTITY;
+        }
+
+        @Path("dynamicallyBoundReader")
+        @POST
+        public String postDynamicallyBoundReader(String input) {
+            return input;
+        }
+
+        @Path("nameBoundReader")
+        @POST
+        @NameBoundReader
+        public String postNameBoundReader(String input) {
+            return input;
+        }
+    }
+
+    @Path("class")
+    @NameBoundWriter
+    public static class ClassBindingResource {
+
+        @Path("nameBoundWriter")
+        @GET
+        public String getNameBoundWriter() {
+            return ENTITY;
+        }
+
+        @Path("nameBoundReader")
+        @POST
+        public String postNameBoundReader(String input) {
+            return input;
+        }
+    }
+
+    @Path("mixed")
+    @NameBoundWriter
+    public static class MixedBindingResource {
+
+        @Path("nameBoundWriterDynamicReader")
+        @POST
+        public String postNameBoundWrDynamicallyBoundReader(String input) {
+            return input;
+        }
+
+        @Path("nameBoundWriterDynamicWriterNameBoundReader")
+        @POST
+        @NameBoundReader
+        public String postNameBoundReWrDynamicallyBoundWriter(String input) {
+            return input;
+        }
+    }
+
+    static final Pattern ReaderMETHOD = Pattern.compile(".*Dynamically.*Reader");
+    static final Pattern WriterMETHOD = Pattern.compile(".*Dynamically.*Writer");
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MethodBindingResource.class, ClassBindingResource.class,
+                MixedBindingResource.class, NameBoundReaderInterceptor.class, NameBoundWriterInterceptor.class).registerInstances(
+                new DynamicFeature() {
+
+                    @Override
+                    public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
+                        if (ReaderMETHOD.matcher(resourceInfo.getResourceMethod().getName()).matches()) {
+                            context.register(DynamicallyBoundReaderInterceptor.class);
+                        }
+                    }
+                },
+                new DynamicFeature() {
+
+                    @Override
+                    public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
+                        if (WriterMETHOD.matcher(resourceInfo.getResourceMethod().getName()).matches()) {
+                            context.register(DynamicallyBoundWriterInterceptor.class);
+                        }
+                    }
+                }
+        );
+    }
+
+    @Test
+    public void testNameBoundReaderOnMethod() {
+        _testReader("method", "nameBoundReader");
+    }
+
+    @Test
+    public void testNameBoundWriterOnMethod() {
+        _testWriter("method", "nameBoundWriter");
+    }
+
+    @Test
+    public void testNameBoundReaderOnClass() {
+        _testReader("class", "nameBoundReader", "nameBoundWriterENTITY");
+    }
+
+    @Test
+    public void testNameBoundWriterOnClass() {
+        _testWriter("class", "nameBoundWriter");
+    }
+
+    @Test
+    public void testDynamicallyBoundReaderOnMethod() {
+        _testReader("method", "dynamicallyBoundReader");
+    }
+
+    @Test
+    public void testDynamicallyBoundWriterOnMethod() {
+        _testWriter("method", "dynamicallyBoundWriter");
+    }
+
+    @Test
+    public void testDynamicReaderOnMethodNamedWriterOnClass() {
+        _testReader("mixed", "nameBoundWriterDynamicReader", "nameBoundWriterdynamicallyBoundReaderENTITY");
+    }
+
+    @Test
+    public void testNameBoundWriterDynamicWriterNameBoundReader() {
+        _testReader("mixed", "nameBoundWriterDynamicWriterNameBoundReader",
+                "dynamicallyBoundWriternameBoundWriternameBoundReaderENTITY");
+    }
+
+    private void _testReader(String root, String id) {
+        _testReader(root, id, id + ENTITY);
+    }
+
+    private void _testReader(String root, String id, String expected) {
+        Response r = target(root + "/" + id).request().post(Entity.entity(ENTITY, MediaType.TEXT_PLAIN));
+        assertEquals(200, r.getStatus());
+        assertEquals(expected, r.readEntity(String.class));
+    }
+
+    private void _testWriter(String root, String id) {
+        _testWriter(root, id, id + ENTITY);
+    }
+
+    private void _testWriter(String root, String id, String expected) {
+        Response r = target(root + "/" + id).request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals(expected, r.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ManagedClientExecutorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ManagedClientExecutorTest.java
new file mode 100644
index 0000000..b043227
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ManagedClientExecutorTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2017, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Type;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.server.ClientBinding;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.Uri;
+import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test, that managed client uses the custom executor service.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class ManagedClientExecutorTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(TestResource.class);
+    }
+
+    /**
+     * Test JAX-RS resource.
+     */
+    @Path(value = "/test")
+    public static class TestResource {
+        /**
+         * Invoke an asynchronous request using the injected managed client.
+         * <p>
+         * The client is configured to use custom message body reader, that checks its worker thread and replaces the original
+         * response with the transfer object containing the thread name. The name is than used to identify if the correct
+         * (custom) executor service is used.
+         *
+         * @return name of the thread, that executed the asynchronous request
+         */
+        @GET
+        @Path("executor")
+        public String managedClientWithExecutor(@ClientA @Uri("http://localhost:9998/") WebTarget target)
+                throws ExecutionException, InterruptedException {
+            Future<ThreadName> nameFuture = target.path("test/dummy").request().async().get(ThreadName.class);
+            return nameFuture.get().getThreadName();
+        }
+
+        /**
+         * Invoke an asynchronous request using the injected managed client.
+         * <p>
+         * The client is configured to use custom message body reader, that uses injected scheduled executor service and
+         * schedules a task, that checks its worker thread name, then it does the same with the injected scheduled executor
+         * service provider (to check that both injection approaches produce same result) and returns both thread names in a
+         * transfer objects. The names are than used to identify if the correct (custom) scheduled executor service(s) is (are)
+         * used.
+         *
+         * @return names of the threads, that executed the scheduled tasks
+         */
+        @GET
+        @Path("scheduledExecutor")
+        public String managedClientWithScheduledExecutor(@ClientA @Uri("http://localhost:9998/") WebTarget target)
+                throws ExecutionException, InterruptedException {
+            Future<SchedulerThreadName> namesFuture = target.path("test/dummy").request().async().get(SchedulerThreadName.class);
+            return namesFuture.get().getThreadNameFromService() + " " + namesFuture.get().getThreadNameFromProvider();
+        }
+
+        /**
+         * Dummy method, that is invoked by the managed client.
+         *
+         * @return dummy response, does not really matter
+         */
+        @GET
+        @Path("dummy")
+        public String dummyMethod() {
+            return "nothing";
+        }
+    }
+
+    /**
+     * Managed client binding.
+     */
+    @ClientBinding(configClass = MyClientAConfig.class)
+    @Documented
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.FIELD, ElementType.PARAMETER})
+    public @interface ClientA {
+    }
+
+    /**
+     * Managed client config.
+     */
+    public static class MyClientAConfig extends ClientConfig {
+
+        public MyClientAConfig() {
+            final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                    .setNameFormat("foo-executor-service-%d")
+                    .build());
+
+            final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder()
+                    .setNameFormat("bar-executor-service")
+                    .build());
+
+            this.register(new ThreadNameReader())
+                    .register(SchedulerThreadNameReader.class)
+                    .executorService(executor)
+                    .scheduledExecutorService(scheduler);
+        }
+    }
+
+    /**
+     * Transfer object for executor service name.
+     */
+    static class ThreadName {
+        private String threadName;
+
+        ThreadName(String threadName) {
+            this.threadName = threadName;
+        }
+
+        String getThreadName() {
+            return threadName;
+        }
+    }
+
+    /**
+     * Transfer object for scheduled executor service(s) name(s), both resolved from directly injected service and via provider.
+     */
+    static class SchedulerThreadName {
+        private String threadNameFromService;
+        private String threadNameFromProvider;
+
+        SchedulerThreadName(String threadNameFromService, String threadNameFromProvider) {
+            this.threadNameFromService = threadNameFromService;
+            this.threadNameFromProvider = threadNameFromProvider;
+        }
+
+        String getThreadNameFromService() {
+            return threadNameFromService;
+        }
+
+        String getThreadNameFromProvider() {
+            return threadNameFromProvider;
+        }
+    }
+
+    /**
+     * Custom reader, that checks its own thread's name and returns it as an entity.
+     */
+    public static class ThreadNameReader implements MessageBodyReader<ThreadName> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public ThreadName readFrom(Class<ThreadName> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                                   MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            return new ThreadName(Thread.currentThread().getName());
+        }
+    }
+
+    /**
+     * Custom reader, that schedules one task on each injected scheduler (directly and via provider) and returns the names of
+     * the threads that executed those tasks as an entity.
+     */
+    public static class SchedulerThreadNameReader implements MessageBodyReader<SchedulerThreadName> {
+
+        @Inject
+        ScheduledExecutorServiceProvider injectedProvider;
+
+        @Inject
+        ScheduledExecutorService injectedService;
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public SchedulerThreadName readFrom(Class<SchedulerThreadName> type, Type genericType, Annotation[] annotations,
+                                            MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
+                                            InputStream entityStream) throws IOException, WebApplicationException {
+
+            AtomicReference<String> nameFromService = new AtomicReference<>();
+            AtomicReference<String> nameFromProvider = new AtomicReference<>();
+            CountDownLatch cdl = new CountDownLatch(2);
+
+            injectedService.schedule(() -> {
+                nameFromService.set(Thread.currentThread().getName());
+                cdl.countDown();
+            }, 1, TimeUnit.MILLISECONDS);
+
+            injectedProvider.getExecutorService().schedule(() -> {
+                nameFromProvider.set(Thread.currentThread().getName());
+                cdl.countDown();
+            }, 1, TimeUnit.MILLISECONDS);
+
+            try {
+                cdl.await(200, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                nameFromService.set("failed");
+                nameFromProvider.set("failed");
+            }
+
+            return new SchedulerThreadName(nameFromService.get(), nameFromProvider.get());
+        }
+    }
+
+    @Test
+    public void testManagedClientExecutor() {
+        final String response = target().path("test/executor").request().get(String.class);
+        Assert.assertEquals("foo-executor-service-0", response);
+    }
+
+    @Test
+    public void testManagedClientScheduledExecutor() {
+        final String response = target().path("test/scheduledExecutor").request().get(String.class);
+        Assert.assertEquals("bar-executor-service bar-executor-service", response);
+        System.out.println(response);
+    }
+}
+
+
+
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MessageBodyProvidersExceptionsTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MessageBodyProvidersExceptionsTest.java
new file mode 100644
index 0000000..5036151
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MessageBodyProvidersExceptionsTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2014, 2018 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.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * JERSEY-2500 reproducer test.
+ *
+ * Tests, that correct exceptions are thrown in case no MessageBodyProvider was matched on server.
+ *
+ * - InternalServerErrorException for MBW (JSR339, chapter 4.2.2, step 7)
+ * - NotSupportedException for MBR (JSR339, chapter 4.2.1, step 6)
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class MessageBodyProvidersExceptionsTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(MessageBodyProvidersExceptionsTest.class.getName());
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                Resource.class,
+                WebAppExceptionMapper.class
+        );
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        @Path("write")
+        @Produces(MediaType.TEXT_PLAIN)
+        public Resource failOnWrite() {
+            return this;
+        }
+
+        @POST
+        @Path("read")
+        @Consumes("foo/bar")
+        @Produces(MediaType.TEXT_PLAIN)
+        public String failOnRead() {
+            return "this-should-never-be-returned";
+        }
+    }
+
+    @Provider
+    public static class WebAppExceptionMapper implements ExceptionMapper<WebApplicationException> {
+
+        @Override
+        public Response toResponse(WebApplicationException exception) {
+            LOGGER.fine("ExceptionMapper was invoked.");
+            // return the exception class name as an entity for further comparison
+            return Response.status(200).header("writer-exception", "after-first-byte").entity(exception.getClass().getName())
+                    .build();
+        }
+    }
+
+    @Test
+    public void testReaderThrowsCorrectException() {
+        Response response = target().path("resource/write").request(MediaType.TEXT_PLAIN).get();
+        assertEquals(200, response.getStatus());
+        String resString = response.readEntity(String.class);
+        // no MBW should have been found, InternalServerErrorException expected
+        assertEquals("javax.ws.rs.InternalServerErrorException", resString);
+    }
+
+    @Test
+    public void testWriterThrowsCorrectException() {
+        Response response = target().path("resource/read").request().post(Entity.entity("Hello, world", "text/plain"));
+        assertEquals(200, response.getStatus());
+        String resString = response.readEntity(String.class);
+        // no MBR should have been found, NotSupportedException expected
+        assertEquals("javax.ws.rs.NotSupportedException", resString);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupDisabledTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupDisabledTest.java
new file mode 100644
index 0000000..44c514a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupDisabledTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 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 javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+import org.junit.Test;
+
+/**
+ * Property {@link ServerProperties#METAINF_SERVICES_LOOKUP_DISABLE} IS set.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class MetainfServicesLookupDisabledTest extends AbstractDisableMetainfServicesLookupTest {
+
+    @Test
+    public void testGet() throws Exception {
+        testGet(500, 415);
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = (ResourceConfig) super.configure();
+        resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
+
+        return resourceConfig;
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupEnabledTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupEnabledTest.java
new file mode 100644
index 0000000..a558f11
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupEnabledTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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 org.junit.Test;
+
+/**
+ * Property {@link org.glassfish.jersey.server.ServerProperties#METAINF_SERVICES_LOOKUP_DISABLE} is NOT set.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class MetainfServicesLookupEnabledTest extends AbstractDisableMetainfServicesLookupTest {
+
+    @Test
+    public void testGet() throws Exception {
+        testGet(200, 200);
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ModelProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ModelProcessorTest.java
new file mode 100644
index 0000000..e0c1c8b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ModelProcessorTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2012, 2018 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.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ExtendedResourceContext;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.model.ModelProcessor;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.model.ResourceMethod;
+import org.glassfish.jersey.server.model.ResourceModel;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test model processor.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+@RunWith(ConcurrentRunner.class)
+public class ModelProcessorTest extends JerseyTest {
+
+    public static class ModelProcessorFeature implements Feature {
+
+        @Override
+        public boolean configure(FeatureContext context) {
+            context.register(SimpleModelProcessor.class);
+            return true;
+        }
+
+        private static class SimpleModelProcessor implements ModelProcessor {
+
+            @Override
+            public ResourceModel processResourceModel(ResourceModel resourceModel, Configuration configuration) {
+                ResourceModel.Builder modelBuilder = new ResourceModel.Builder(false);
+                final Resource modelResource = Resource.from(ModelResource.class);
+                modelBuilder.addResource(modelResource);
+
+                for (final Resource resource : resourceModel.getRootResources()) {
+                    Resource newResource = enhanceResource(resource);
+                    modelBuilder.addResource(newResource);
+                }
+
+                return modelBuilder.build();
+            }
+
+            private Resource enhanceResource(final Resource resource) {
+                final Resource.Builder resBuilder = Resource.builder(resource);
+                boolean optionsFound = false;
+                for (ResourceMethod resourceMethod : resource.getResourceMethods()) {
+                    if (resourceMethod.getHttpMethod().equals("OPTIONS")) {
+                        optionsFound = true;
+                    }
+                }
+                if (!optionsFound) {
+
+                    resBuilder.addMethod("OPTIONS").produces(MediaType.TEXT_PLAIN_TYPE)
+                            .handledBy(new Inflector<ContainerRequestContext, String>() {
+                                @Override
+                                public String apply(ContainerRequestContext containerRequestContext) {
+                                    return resource.getPath();
+                                }
+                            });
+                }
+
+                final Inflector<ContainerRequestContext, Object> inflector = new Inflector<ContainerRequestContext, Object>() {
+
+                    @Override
+                    public Object apply(ContainerRequestContext requestContext) {
+                        StringBuilder sb = new StringBuilder();
+                        sb.append("my-resource:");
+                        final String path = resource.getPath();
+                        sb.append(path == null ? "<no-path>" : path + ",");
+
+                        for (ResourceMethod resourceMethod : sortResourceMethods(resource.getResourceMethods())) {
+                            sb.append(resourceMethod.getHttpMethod()).append("=").append("")
+                                    .append(resourceMethod.getInvocable().getHandlingMethod().getName()).append("|");
+                        }
+                        return sb.toString();
+                    }
+                };
+
+                resBuilder.addChildResource("my-resource")
+                        .addMethod("GET")
+                        .produces(MediaType.TEXT_PLAIN_TYPE)
+                        .handledBy(inflector).build();
+
+                return resBuilder.build();
+            }
+
+            @Override
+            public ResourceModel processSubResource(ResourceModel subResource, Configuration configuration) {
+                final Resource resource = enhanceResource(subResource.getResources().get(0));
+                return new ResourceModel.Builder(true).addResource(resource).build();
+            }
+        }
+
+        @Path("model")
+        public static class ModelResource {
+
+            @Context
+            ExtendedResourceContext resourceContext;
+
+            @GET
+            public String get() {
+                final ResourceModel resourceModel = resourceContext.getResourceModel();
+                StringBuilder sb = new StringBuilder();
+                List<Resource> sortedResources = resourceModel.getRootResources();
+                Collections.sort(sortedResources, new Comparator<Resource>() {
+                    @Override
+                    public int compare(Resource o1, Resource o2) {
+                        final String path1 = o1.getPath() == null ? "" : o1.getPath();
+                        final String path2 = o2.getPath() == null ? "" : o2.getPath();
+                        return path1.compareTo(path2);
+                    }
+                });
+                for (Resource resource : sortedResources) {
+                    final String path = resource.getPath();
+                    sb.append(path == null ? "<no-path>" : path).append("|");
+                }
+
+                return sb.toString();
+            }
+        }
+    }
+
+    private static List<ResourceMethod> sortResourceMethods(List<ResourceMethod> resourceMethods) {
+        List<ResourceMethod> newList = new ArrayList<>(resourceMethods);
+        Collections.sort(newList, (o1, o2) -> o1.getHttpMethod().compareTo(o2.getHttpMethod()));
+        return newList;
+    }
+
+    @Path("a")
+    public static class ResourceA {
+
+        @GET
+        public String getFromA() {
+            return "a-get";
+        }
+
+        @POST
+        public String postFromA(String entity) {
+            return "a-post";
+        }
+
+        @GET
+        @Path("child")
+        public String getChild() {
+            return "a-child-get";
+        }
+
+        @Path("locator")
+        public SubResource locatorFromA() {
+            return new SubResource();
+        }
+    }
+
+    public static class SubResource {
+
+        @GET
+        public String getFromSubResource() {
+            return "sub-get";
+        }
+    }
+
+    @Path("b")
+    public static class ResourceB {
+
+        @GET
+        public String getFromB() {
+            return "b-get";
+        }
+
+        @OPTIONS
+        public String optionsFromB() {
+            return "b-options";
+        }
+
+        @Path("locator")
+        public SubResource locatorFromB() {
+            return new SubResource();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(ResourceA.class, ResourceB.class, ModelProcessorFeature.class);
+        resourceConfig.property(ServerProperties.WADL_FEATURE_DISABLE, true);
+        return resourceConfig;
+    }
+
+    @Test
+    public void testResourceAGet() {
+        Response response = target("/a").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("a-get", entity);
+    }
+
+    @Test
+    public void testResourceAPost() {
+        Response response = target("/a").request().post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("a-post", entity);
+    }
+
+    @Test
+    public void testResourceAOptions() {
+        Response response = target("/a").request(MediaType.TEXT_PLAIN_TYPE).options();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("a", entity);
+    }
+
+    @Test
+    public void testResourceAChildGet() {
+        Response response = target("/a/child").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("a-child-get", entity);
+    }
+
+    @Test
+    public void testResourceALocatorGet() {
+        Response response = target("/a/locator").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("sub-get", entity);
+    }
+
+    @Test
+    public void testResourceALocatorOptions() {
+        Response response = target("/a/locator").request(MediaType.TEXT_PLAIN_TYPE).options();
+        assertEquals(204, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("", entity);
+    }
+
+    @Test
+    public void testResourceBGet() {
+        Response response = target("/b").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("b-get", entity);
+    }
+
+    @Test
+    public void testResourceBOptions() {
+        Response response = target("/b").request(MediaType.TEXT_PLAIN_TYPE).options();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("b-options", entity);
+    }
+
+    @Test
+    public void testResourceBLocatorGet() {
+        Response response = target("/b/locator").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("sub-get", entity);
+    }
+
+    @Test
+    public void testResourceBLocatorOptions() {
+        Response response = target("/b/locator").request(MediaType.TEXT_PLAIN_TYPE).options();
+        assertEquals(204, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("", entity);
+    }
+
+    @Test
+    public void testResourceAMyResource() {
+        Response response = target("/a/my-resource").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("my-resource:a,GET=getFromA|POST=postFromA|", entity);
+    }
+
+    @Test
+    public void testResourceALocatorMyResource() {
+        Response response = target("/a/locator/my-resource").request(MediaType.TEXT_PLAIN_TYPE).get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("my-resource:<no-path>GET=getFromSubResource|", entity);
+    }
+
+    @Test
+    public void testResourceBMyResource() {
+        Response response = target("/b/my-resource").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("my-resource:b,GET=getFromB|OPTIONS=optionsFromB|", entity);
+    }
+
+    @Test
+    public void testInfo() {
+        Response response = target("/model").request().get();
+        assertEquals(200, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("a|b|model|", entity);
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/NameBindingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/NameBindingTest.java
new file mode 100644
index 0000000..02a740c
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/NameBindingTest.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test-suite ensuring the correct functionality of name binding.
+ *
+ * @author Miroslav Fuksa
+ * @author Michal Gajdos
+ */
+@RunWith(ConcurrentRunner.class)
+public class NameBindingTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, FooResource.class, BarResource.class, FooBarResource.class, FooFilter.class,
+                BarFilter.class, FooBarFilter.class, PreMatchingFooFilter.class);
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        public String noBinding() {
+            return "noBinding";
+        }
+
+        @GET
+        @FooBinding
+        @Path("foo")
+        public String foo() {
+            return "foo";
+        }
+
+        @GET
+        @BarBinding
+        @Path("bar")
+        public String bar() {
+            return "bar";
+        }
+
+        @GET
+        @FooBinding
+        @BarBinding
+        @Path("foobar")
+        public String foobar() {
+            return "foobar";
+        }
+
+        @GET
+        @Path("preMatchingNameBinding")
+        public String preMatchingNameBinding(@HeaderParam("header") @DefaultValue("bar") final String header) {
+            return header;
+        }
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface FooBinding {
+
+    }
+
+    @NameBinding
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface BarBinding {
+
+    }
+
+    @FooBinding
+    public static class FooFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws
+                IOException {
+            responseContext.getHeaders().add(this.getClass().getSimpleName(), "called");
+        }
+    }
+
+    @PreMatching
+    @FooBinding
+    public static class PreMatchingFooFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+            requestContext.getHeaders().putSingle("header", "foo");
+        }
+    }
+
+    @BarBinding
+    public static class BarFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws
+                IOException {
+            responseContext.getHeaders().add(this.getClass().getSimpleName(), "called");
+        }
+    }
+
+    @FooBinding
+    @BarBinding
+    public static class FooBarFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws
+                IOException {
+            responseContext.getHeaders().add(this.getClass().getSimpleName(), "called");
+        }
+    }
+
+    private static final Set<Class<?>> FILTERS = initialize();
+
+    private static Set<Class<?>> initialize() {
+        final Set<Class<?>> set = new HashSet<>();
+        set.add(FooFilter.class);
+        set.add(BarFilter.class);
+        set.add(FooBarFilter.class);
+        return set;
+    }
+
+    private void checkCalled(final Response response, final Class<?>... filtersThatShouldBeCalled) {
+        final Set<Class<?>> positiveFilters = Arrays.stream(filtersThatShouldBeCalled).collect(Collectors.toSet());
+        for (final Class<?> filter : FILTERS) {
+            if (positiveFilters.contains(filter)) {
+                assertEquals("Filter '" + filter.getSimpleName() + "' should be called.", "called", response.getHeaders()
+                        .getFirst(filter.getSimpleName()));
+            } else {
+                assertNull("Filter '" + filter.getSimpleName() + "' should not be called.", response.getHeaders().get(filter
+                        .getSimpleName()));
+            }
+        }
+    }
+
+    private Response _getResponse(final String path) {
+        final Response response = target().path(path).request().get();
+        assertEquals(200, response.getStatus());
+        return response;
+    }
+
+    @Test
+    public void testResourceNoBinding() {
+        checkCalled(_getResponse("resource"));
+    }
+
+    @Test
+    public void testResourceFooBinding() {
+        checkCalled(_getResponse("resource/foo"), FooFilter.class);
+    }
+
+    /**
+     * Reproducer for JERSEY-2739. Name bound annotation on a pre-matching filter should be ignored and the filter should be
+     * invoked for each resource method (globally).
+     */
+    @Test
+    public void preMatchingNameBinding() {
+        final Response response = _getResponse("resource/preMatchingNameBinding");
+
+        // Request filter - applied, even when the filter is name bound and the resource method is not.
+        assertThat("Name binding on a @PreMatching filter not ignored.", response.readEntity(String.class), is("foo"));
+    }
+
+    @Test
+    public void testResourceBarBinding() {
+        checkCalled(_getResponse("resource/bar"), BarFilter.class);
+    }
+
+    @Test
+    public void testResourceFooBarBinding() {
+        checkCalled(_getResponse("resource/foobar"), FooFilter.class, BarFilter.class, FooBarFilter.class);
+    }
+
+    @Path("foo-resource")
+    @FooBinding
+    public static class FooResource extends Resource {
+
+    }
+
+    @Test
+    public void testFooResourceNoBinding() {
+        checkCalled(_getResponse("foo-resource"), FooFilter.class);
+    }
+
+    @Test
+    public void testFooResourceFooBinding() {
+        checkCalled(_getResponse("foo-resource/foo"), FooFilter.class);
+    }
+
+    @Test
+    public void testFooResourceBarBinding() {
+        checkCalled(_getResponse("foo-resource/bar"), FooFilter.class, BarFilter.class, FooBarFilter.class);
+    }
+
+    @Test
+    public void testFooResourceFooBarBinding() {
+        checkCalled(_getResponse("foo-resource/foobar"), FooFilter.class, BarFilter.class, FooBarFilter.class);
+    }
+
+    @Path("bar-resource")
+    @BarBinding
+    public static class BarResource extends Resource {
+
+    }
+
+    @Test
+    public void testBarResourceNoBinding() {
+        checkCalled(_getResponse("bar-resource"), BarFilter.class);
+    }
+
+    @Test
+    public void testBarResourceFooBinding() {
+        checkCalled(_getResponse("bar-resource/foo"), BarFilter.class, FooFilter.class, FooBarFilter.class);
+    }
+
+    @Test
+    public void testBarResourceBarBinding() {
+        checkCalled(_getResponse("bar-resource/bar"), BarFilter.class);
+    }
+
+    @Test
+    public void testBarResourceFooBarBinding() {
+        checkCalled(_getResponse("bar-resource/foobar"), BarFilter.class, FooFilter.class, FooBarFilter.class);
+    }
+
+    @Path("foobar-resource")
+    @BarBinding
+    @FooBinding
+    public static class FooBarResource extends Resource {
+
+    }
+
+    @Test
+    public void testFooBarResourceNoBinding() {
+        checkCalled(_getResponse("foobar-resource"), BarFilter.class, FooFilter.class, FooBarFilter.class);
+    }
+
+    @Test
+    public void testFooBarResourceFooBinding() {
+        checkCalled(_getResponse("foobar-resource/foo"), BarFilter.class, FooFilter.class, FooBarFilter.class);
+    }
+
+    @Test
+    public void testFooBarResourceBarBinding() {
+        checkCalled(_getResponse("foobar-resource/bar"), BarFilter.class, FooFilter.class, FooBarFilter.class);
+    }
+
+    @Test
+    public void testFooBarResourceFooBarBinding() {
+        checkCalled(_getResponse("foobar-resource/foobar"), BarFilter.class, FooFilter.class, FooBarFilter.class);
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterPriorityTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterPriorityTest.java
new file mode 100644
index 0000000..0105506
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterPriorityTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, 2018 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 java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.GET;
+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 javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.typeCompatibleWith;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ParamConverterPriorityTest extends JerseyTest {
+
+    private static final List<Class<? extends ParamConverterProvider>> CONVERTER_PROVIDER_CALL_ORDER = new ArrayList<>();
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ParamConverterPriorityResource.class,
+                                  MyParamConverterProvider1.class,
+                                  MyParamConverterProvider2.class,
+                                  MyParamConverterProvider3.class);
+    }
+
+    @Test
+    public void test() {
+        Response response = target().queryParam("test", "value").request().get();
+
+        assertThat(CONVERTER_PROVIDER_CALL_ORDER.size(), greaterThanOrEqualTo(3));
+        assertThat(CONVERTER_PROVIDER_CALL_ORDER.get(0), typeCompatibleWith(MyParamConverterProvider2.class));
+        assertThat(CONVERTER_PROVIDER_CALL_ORDER.get(1), typeCompatibleWith(MyParamConverterProvider1.class));
+        assertThat(CONVERTER_PROVIDER_CALL_ORDER.get(2), typeCompatibleWith(MyParamConverterProvider3.class));
+    }
+
+    @Path("/")
+    public static class ParamConverterPriorityResource {
+
+        @GET
+        public String get(@QueryParam("test") String param) {
+            return param;
+        }
+    }
+
+    @Priority(200)
+    public static class MyParamConverterProvider1 implements ParamConverterProvider {
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            CONVERTER_PROVIDER_CALL_ORDER.add(this.getClass());
+            return null;
+        }
+    }
+
+    @Priority(100)
+    public static class MyParamConverterProvider2 implements ParamConverterProvider {
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            CONVERTER_PROVIDER_CALL_ORDER.add(this.getClass());
+            return null;
+        }
+    }
+
+    @Priority(300)
+    public static class MyParamConverterProvider3 implements ParamConverterProvider {
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            CONVERTER_PROVIDER_CALL_ORDER.add(this.getClass());
+            return null;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterTest.java
new file mode 100644
index 0000000..6ef6bc6
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterTest.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2012, 2018 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 java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+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.Ignore;
+import org.junit.Test;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests {@link ParamConverter param converters} as e2e test.
+ *
+ * @author Miroslav Fuksa
+ */
+public class ParamConverterTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, MyParamProvider.class, MyStringParamProvider.class);
+    }
+
+    @Test
+    public void testMyBeanParam() {
+        Form form = new Form();
+        form.param("form", "formParam");
+        final Response response = target()
+                .path("resource/myBean").path("pathParam")
+                .matrixParam("matrix", "matrixParam")
+                .queryParam("query", "queryParam")
+                .request()
+                .header("header", "headerParam")
+                .cookie("cookie", "cookieParam")
+                .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+
+        final String str = response.readEntity(String.class);
+        assertEquals("*pathParam*_*matrixParam*_*queryParam*_*headerParam*_*cookieParam*_*formParam*", str);
+    }
+
+    @Test
+    public void testListOfMyBeanParam() {
+        final Response response = target().path("resource/myBean/list")
+                .queryParam("q", "A")
+                .queryParam("q", "B")
+                .queryParam("q", "C")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertEquals("*A**B**C*", str);
+    }
+
+    @Test
+    public void testSetOfMyBeanParam() {
+        final Response response = target().path("resource/myBean/set")
+                .queryParam("q", "A")
+                .queryParam("q", "B")
+                .queryParam("q", "C")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertThat(str, containsString("*A*"));
+        assertThat(str, containsString("*B*"));
+        assertThat(str, containsString("*C*"));
+    }
+
+    @Test
+    public void testSortedSetOfMyBeanParam() {
+        final Response response = target().path("resource/myBean/sortedset")
+                .queryParam("q", "A")
+                .queryParam("q", "B")
+                .queryParam("q", "C")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertEquals("*A**B**C*", str);
+    }
+
+    @Test
+    public void testStringParam() {
+        Form form = new Form();
+        form.param("form", "formParam");
+        final Response response = target()
+                .path("resource/string").path("pathParam")
+                .matrixParam("matrix", "matrixParam")
+                .queryParam("query", "queryParam")
+                .request()
+                .header("header", "headerParam")
+                .cookie("cookie", "cookieParam")
+                .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+
+        final String str = response.readEntity(String.class);
+        assertEquals("-pathParam-_-matrixParam-_-queryParam-_-headerParam-_-cookieParam-_-formParam-", str);
+    }
+
+    @Test
+    @Ignore("TODO: ParamConversion not yet implemented in the ResponseBuilder (JERSEY-1385).")
+    // TODO: JERSEY-1385: after clarifying with spec the ResponseBuilder paramconversion should be finished (or removed)
+    public void testStringParamInResponse() {
+        final Response response = target().path("resource/response").request().get();
+        assertEquals("-:res-head:-", response.getHeaderString("response-header"));
+
+    }
+
+    @Test
+    public void testMyBeanFormParamDefault() {
+        Form form = new Form();
+        Response response = target().path("resource/myBeanFormDefault")
+                .request().post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+        String str = response.readEntity(String.class);
+        assertEquals("*form-default*", str);
+    }
+
+    @Test
+    public void testMyBeanQueryParamDefault() {
+        final Response response = target().path("resource/myBeanQueryDefault")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertEquals("*query-default*", str);
+    }
+
+    @Test
+    public void testMyBeanMatrixParamDefault() {
+        final Response response = target().path("resource/myBeanMatrixDefault")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertEquals("*matrix-default*", str);
+    }
+
+    @Test
+    public void testMyBeanCookieParamDefault() {
+        final Response response = target().path("resource/myBeanCookieDefault")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertEquals("*cookie-default*", str);
+    }
+
+    @Test
+    public void testMyBeanHeaderParamDefault() {
+        final Response response = target().path("resource/myBeanHeaderDefault")
+                .request().get();
+        final String str = response.readEntity(String.class);
+        assertEquals("*header-default*", str);
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @POST
+        @Path("myBean/{path}")
+        public String postMyBean(@PathParam("path") MyBean pathParam, @MatrixParam("matrix") MyBean matrix,
+                                 @QueryParam("query") MyBean query, @HeaderParam("header") MyBean header,
+                                 @CookieParam("cookie") MyBean cookie, @FormParam("form") MyBean form) {
+            return pathParam.getValue() + "_" + matrix.getValue() + "_" + query.getValue() + "_" + header.getValue() + "_"
+                    + cookie.getValue() + "_" + form.getValue();
+        }
+
+        @GET
+        @Path("myBean/list")
+        public String postMyBean(@QueryParam("q") List<MyBean> query) {
+            StringBuilder sb = new StringBuilder();
+            for (MyBean bean : query) {
+                sb.append(bean.getValue());
+            }
+
+            return sb.toString();
+        }
+
+        @GET
+        @Path("myBean/set")
+        public String postMyBean(@QueryParam("q") Set<MyBean> query) {
+            StringBuilder sb = new StringBuilder();
+            for (MyBean bean : query) {
+                sb.append(bean.getValue());
+            }
+
+            return sb.toString();
+        }
+
+        @GET
+        @Path("myBean/sortedset")
+        public String postMyBean(@QueryParam("q") SortedSet<MyBean> query) {
+            StringBuilder sb = new StringBuilder();
+            for (MyBean bean : query) {
+                sb.append(bean.getValue());
+            }
+
+            return sb.toString();
+        }
+
+        @POST
+        @Path("myBeanFormDefault")
+        public String postMyBeanFormDefault(@DefaultValue("form-default") @FormParam("form") MyBean pathParam) {
+            return pathParam.getValue();
+        }
+
+        @GET
+        @Path("myBeanQueryDefault")
+        public String getMyBeanQueryDefault(@DefaultValue("query-default") @QueryParam("q") MyBean queryParam) {
+            return queryParam.getValue();
+        }
+
+        @GET
+        @Path("myBeanMatrixDefault")
+        public String getMyBeanMatrixDefault(@DefaultValue("matrix-default") @MatrixParam("m") MyBean matrixParam) {
+            return matrixParam.getValue();
+        }
+
+        @GET
+        @Path("myBeanCookieDefault")
+        public String getMyBeanCookieDefault(@DefaultValue("cookie-default") @CookieParam("c") MyBean cookieParam) {
+            return cookieParam.getValue();
+        }
+
+        @GET
+        @Path("myBeanHeaderDefault")
+        public String getMyBeanHeaderDefault(@DefaultValue("header-default") @HeaderParam("h") MyBean headerParam) {
+            return headerParam.getValue();
+        }
+
+        @POST
+        @Path("string/{path}")
+        public String postString(@PathParam("path") String pathParam, @MatrixParam("matrix") String matrix,
+                                 @QueryParam("query") String query, @HeaderParam("header") String header,
+                                 @CookieParam("cookie") String cookie, @FormParam("form") String form) {
+            return pathParam + "_" + matrix + "_" + query + "_" + header + "_"
+                    + cookie + "_" + form;
+        }
+
+        @GET
+        @Path("q")
+        public String get(@QueryParam("query") String query) {
+            return query;
+        }
+
+        @GET
+        @Path("response")
+        public Response getResponse() {
+            return Response.ok().header("response-header", "res-head").entity("anything").build();
+        }
+    }
+
+    public static class MyParamProvider implements ParamConverterProvider {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            if (rawType != MyBean.class) {
+                return null;
+            }
+
+            return (ParamConverter<T>) new ParamConverter<MyBean>() {
+
+                @Override
+                public MyBean fromString(String value) throws IllegalArgumentException {
+                    final MyBean myBean = new MyBean();
+                    myBean.setValue("*" + value + "*");
+                    return myBean;
+                }
+
+                @Override
+                public String toString(MyBean bean) throws IllegalArgumentException {
+                    return "*:" + bean.getValue() + ":*";
+                }
+
+            };
+        }
+    }
+
+    public static class MyStringParamProvider implements ParamConverterProvider {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            if (rawType != String.class) {
+                return null;
+            }
+
+            return (ParamConverter<T>) new ParamConverter<String>() {
+
+                @Override
+                public String fromString(String value) throws IllegalArgumentException {
+                    return "-" + value + "-";
+                }
+
+                @Override
+                public String toString(String str) throws IllegalArgumentException {
+                    return "-:" + str + ":-";
+                }
+
+            };
+        }
+    }
+
+    public static class MyBean implements Comparable<MyBean> {
+
+        private String value;
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public String toString() {
+            return "MyBean{"
+                    + "value='" + value + '\''
+                    + '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof MyBean)) {
+                return false;
+            }
+
+            MyBean myBean = (MyBean) o;
+
+            return !(value != null ? !value.equals(myBean.value) : myBean.value != null);
+        }
+
+        @Override
+        public int hashCode() {
+            return value != null ? value.hashCode() : 0;
+        }
+
+        @Override
+        public int compareTo(MyBean o) {
+            return value.compareTo(o.value);
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PathEncodingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PathEncodingTest.java
new file mode 100644
index 0000000..19abcc3
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PathEncodingTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Testing encoding of {@Path path annotations}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class PathEncodingTest extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(PercentEncodedTest.class, AsteriskResource.class);
+    }
+
+    @Test
+    public void test1() {
+        assertEquals("ok", target().path("test").path("[]").request().get(String.class));
+    }
+
+    @Test
+    public void test2() {
+        assertEquals("ok", target().path("test").path("%5b%5d").request().get(String.class));
+    }
+
+    @Test
+    public void test3() {
+        assertEquals("ok", target().path("test").path("%5b%5D").request().get(String.class));
+    }
+
+    @Test
+    public void testComplex1() {
+        assertEquals("a-ok", target().path("test").path("test/a/[]").request().get(String.class));
+    }
+
+    @Test
+    public void testComplex2() {
+        assertEquals("a-ok", target().path("test").path("test/a/%5b%5D").request().get(String.class));
+    }
+
+    @Test
+    public void testComplex3() {
+        final Response res = target().path("test").path("test/a/path/%5b%5d").request().get();
+        assertEquals(200, res.getStatus());
+        assertEquals("a-ok", res.readEntity(String.class));
+    }
+
+    @Test
+    public void testNotFound() {
+        final Response res = target().path("test").path("test/a/path/%5ab").request().get();
+        assertEquals(404, res.getStatus());
+    }
+
+    @Test
+    public void testComplex4() {
+        assertEquals("a-ok", target().path("test").path("test/a/path/[]").request().get(String.class));
+    }
+
+    @Test
+    public void testSlash() {
+        assertEquals("ok", target().path("test/slash/").request().get(String.class));
+    }
+
+    @Test
+    public void testWithoutSlash() {
+        assertEquals("ok", target().path("test/slash").request().get(String.class));
+    }
+
+    @Test
+    public void testAsteriskInPath() {
+        Response response = target().path("*").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("ok", response.readEntity(String.class));
+    }
+
+    @Path("*")
+    public static class AsteriskResource {
+        @GET
+        public String get() {
+            return "ok";
+        }
+    }
+
+    @Path("test")
+    public static class PercentEncodedTest {
+
+        @GET
+        @Path("[]")
+        public String simple() {
+            return "ok";
+        }
+
+        @GET
+        @Path("slash/")
+        public String slash(@Context UriInfo uri) {
+            return "ok";
+        }
+
+        @GET
+        @Path("test/{a : .* }/[]")
+        public String complex(@PathParam("a") String a) {
+            return a + "-ok";
+        }
+
+        @GET
+        @Path("test/{a : .* }/path/%5b%5D")
+        public String complex2(@PathParam("a") String a) {
+            return a + "-ok";
+        }
+
+
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PerRequestLifecycleTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PerRequestLifecycleTest.java
new file mode 100644
index 0000000..be23404
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PerRequestLifecycleTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2014, 2018 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.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author Marc Hadley
+ */
+public class PerRequestLifecycleTest extends JerseyTest {
+
+    /**
+     * Enum representing the actual state of the resource in the lifecycle.
+     */
+    private static enum ResourceState {
+        NEW,
+        CONSTRUCTED,
+        DESTROYED
+    }
+
+    @Path("/post-construct")
+    public static class PostConstructResource {
+        private int count;
+
+        @Context
+        Configuration configuration;
+
+        public PostConstructResource() {
+            this.count = 0;
+        }
+
+        @PostConstruct
+        public void postConstruct() {
+            assertNotNull(configuration);
+            count++;
+        }
+
+        @GET
+        public String doGet() {
+            return Integer.toString(count);
+        }
+    }
+
+    private static ResourceState preDestroyState = ResourceState.NEW;
+    private static CountDownLatch preDestroyCdl = new CountDownLatch(1);
+
+    @Path("pre-destroy")
+    public static class PreDestroyResource {
+        public PreDestroyResource() throws IOException {
+            preDestroyState = ResourceState.CONSTRUCTED;
+        }
+
+        @GET
+        public String getFileName() {
+            return preDestroyState.name();
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            assertEquals(ResourceState.CONSTRUCTED, preDestroyState);
+            preDestroyState = ResourceState.DESTROYED;
+            preDestroyCdl.countDown();
+        }
+    }
+
+    @Path("referred")
+    public static class ReferredToResource {
+        @Path("sub")
+        public ReferencingOfResource get() {
+            return new ReferencingOfResource();
+        }
+    }
+
+    private static ResourceState prePostState = ResourceState.NEW;
+    private static CountDownLatch prePostCdl = new CountDownLatch(1);
+
+    @Path("pre-post")
+    public static class PreDestroyPostConstructResource {
+
+        @PostConstruct
+        public void postConstruct() throws IOException {
+            prePostState = ResourceState.CONSTRUCTED;
+        }
+
+        @GET
+        public String getState() {
+            return prePostState.name();
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            assertEquals(ResourceState.CONSTRUCTED, prePostState);
+            prePostState = ResourceState.DESTROYED;
+            prePostCdl.countDown();
+        }
+    }
+
+    private static ResourceState prePostPrivateState = ResourceState.NEW;
+    private static CountDownLatch prePostPrivateCdl = new CountDownLatch(1);
+
+    @Path("pre-post-private")
+    public static class PreDestroyPostConstructResourcePrivate {
+
+        @PostConstruct
+        private void postConstruct() throws IOException {
+            prePostPrivateState = ResourceState.CONSTRUCTED;
+        }
+
+        @GET
+        public String getState() {
+            return prePostPrivateState.name();
+        }
+
+        @PreDestroy
+        private void preDestroy() {
+            assertEquals(ResourceState.CONSTRUCTED, prePostPrivateState);
+            prePostPrivateState = ResourceState.DESTROYED;
+            prePostPrivateCdl.countDown();
+        }
+    }
+
+    private static ResourceState prePostProtectedState = ResourceState.NEW;
+    private static CountDownLatch prePostProtectedCdl = new CountDownLatch(1);
+
+    @Path("pre-post-protected")
+    public static class PreDestroyPostConstructResourceProtected {
+
+        @PostConstruct
+        protected void postConstruct() throws IOException {
+            prePostProtectedState = ResourceState.CONSTRUCTED;
+        }
+
+        @GET
+        public String getState() {
+            return prePostProtectedState.name();
+        }
+
+        @PreDestroy
+        protected void preDestroy() {
+            assertEquals(ResourceState.CONSTRUCTED, prePostProtectedState);
+            prePostProtectedState = ResourceState.DESTROYED;
+            prePostProtectedCdl.countDown();
+        }
+    }
+
+    private static ResourceState inheritedState = ResourceState.NEW;
+    private static CountDownLatch inheritedCdl = new CountDownLatch(1);
+
+    public abstract static class PostConstructResourceInherited {
+        @PostConstruct
+        private void postConstruct() throws IOException {
+            inheritedState = ResourceState.CONSTRUCTED;
+        }
+
+        @GET
+        public String getState() {
+            return inheritedState.name();
+        }
+    }
+
+
+    @Path("inherited")
+    public static class PreDestroyResourceInherited extends PostConstructResourceInherited {
+
+        @PreDestroy
+        private void preDestroy() {
+            assertEquals(ResourceState.CONSTRUCTED, inheritedState);
+            inheritedState = ResourceState.DESTROYED;
+            inheritedCdl.countDown();
+        }
+    }
+
+    public static class ReferencingOfResource {
+        @GET
+        public String get(@Context final ResourceContext rc) {
+            final ReferredToResource r1 = rc.getResource(ReferredToResource.class);
+            final ReferredToResource r2 = rc.getResource(ReferredToResource.class);
+            assertEquals(r1, r2);
+            return "GET";
+        }
+    }
+
+    @Override
+    public ResourceConfig configure() {
+        return new ResourceConfig(PreDestroyPostConstructResource.class, PreDestroyPostConstructResourcePrivate.class,
+                PreDestroyPostConstructResourceProtected.class, PreDestroyResourceInherited.class,
+                ReferredToResource.class, PreDestroyResource.class, PostConstructResource.class);
+    }
+
+    @Test
+    public void testPostConstructResource() {
+        final WebTarget target = target().path("post-construct");
+        assertEquals("1", target.request().get(String.class));
+        assertEquals("1", target.request().get(String.class));
+        assertEquals("1", target.request().get(String.class));
+    }
+
+    @Test
+    public void testPreDestroyResource() throws InterruptedException {
+        final String s = target().path("pre-destroy").request().get(String.class);
+        assertEquals(ResourceState.CONSTRUCTED.name(), s);
+        preDestroyCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertEquals(ResourceState.DESTROYED, preDestroyState);
+    }
+
+    @Test
+    public void testReferredToResource() {
+        assertEquals("GET", target().path("referred/sub").request().get(String.class));
+    }
+
+
+    @Test
+    public void testPreDestroyPostCreateResource() throws InterruptedException {
+        final String s = target().path("pre-post").request().get(String.class);
+        assertEquals(ResourceState.CONSTRUCTED.name(), s);
+        prePostCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertEquals(ResourceState.DESTROYED, prePostState);
+    }
+
+    @Test
+    public void testPreDestroyPostCreateResourcePrivate() throws InterruptedException {
+        final String s = target().path("pre-post-private").request().get(String.class);
+        assertEquals(ResourceState.CONSTRUCTED.name(), s);
+        prePostPrivateCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertEquals(ResourceState.DESTROYED, prePostPrivateState);
+    }
+
+    @Test
+    public void testPreDestroyPostCreateResourceProtected() throws InterruptedException {
+        final String s = target().path("pre-post-protected").request().get(String.class);
+        assertEquals(ResourceState.CONSTRUCTED.name(), s);
+        prePostProtectedCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertEquals(ResourceState.DESTROYED, prePostProtectedState);
+    }
+
+    @Test
+    public void testPreDestroyPostCreateResourceInherited() throws InterruptedException {
+        final String s = target().path("inherited").request().get(String.class);
+        assertEquals(ResourceState.CONSTRUCTED.name(), s);
+        inheritedCdl.await(1000, TimeUnit.MILLISECONDS);
+        assertEquals(ResourceState.DESTROYED, inheritedState);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PostConstructTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PostConstructTest.java
new file mode 100644
index 0000000..d3812ba
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PostConstructTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2013, 2018 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.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.PostConstruct;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Testing that {@link Context injection} is done before invoking method annotated with {@link PostConstruct}.
+ *
+ * @author Michal Gajdos
+ */
+public class PostConstructTest extends JerseyTest {
+
+    @Path("/")
+    public static class Resource {
+
+        private int value;
+
+        @Context
+        private UriInfo uri;
+
+        @Context
+        private Configuration configuration;
+
+        @PostConstruct
+        public void postConstruct() {
+            value = configuration != null ? 1 : 0;
+        }
+
+        @GET
+        public String get() {
+            return "value=" + value + "|" + configuration.getProperty("value");
+        }
+    }
+
+    public static class MyApplication extends Application {
+
+        private int value;
+
+        @Context
+        private UriInfo uriInfo;
+
+        @PostConstruct
+        public void postConstruct() {
+            value = uriInfo != null ? 1 : 0;
+        }
+
+        @Override
+        public Set<Class<?>> getClasses() {
+            return Collections.singleton(Resource.class);
+        }
+
+        @Override
+        public Map<String, Object> getProperties() {
+            final HashMap<String, Object> map = new HashMap<>();
+            map.put("value", value);
+            return map;
+        }
+    }
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        // If strategy is not IMMEDIATE then test will fail even before @Before setup method invocation.
+        // It has no other reason then just run the tests in IMMEDIATE strategy.
+        if (Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            return DeploymentContext.newInstance(MyApplication.class);
+        } else {
+            return DeploymentContext.newInstance(new ResourceConfig());
+        }
+    }
+
+    @Test
+    public void testApplicationResourcePostConstruct() throws Exception {
+        assertEquals("value=1|1", target().request().get(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PrimitiveTypesTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PrimitiveTypesTest.java
new file mode 100644
index 0000000..7dabf96
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/PrimitiveTypesTest.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2012, 2018 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.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Tests primitive types as entity.
+ *
+ * @author Miroslav Fuksa
+ */
+@RunWith(ConcurrentRunner.class)
+public class PrimitiveTypesTest extends JerseyTest {
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testInteger() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Integer").request().post(Entity.entity(5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Integer(6), response.readEntity(Integer.class));
+    }
+
+    @Test
+    public void testPrimitiveInt() {
+        WebTarget web = target().path("test");
+        Response response = web.path("int").request().post(Entity.entity(5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Integer(6), response.readEntity(Integer.class));
+    }
+
+    @Test
+    public void testPrimitiveIntNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("int").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+
+    @Test
+    public void testLong() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Long").request().post(Entity.entity(5L, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Long(6), response.readEntity(Long.class));
+    }
+
+    @Test
+    public void testPrimitiveLong() {
+        WebTarget web = target().path("test");
+        Response response = web.path("long").request().post(Entity.entity(5L, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(6L, (long) response.readEntity(long.class));
+    }
+
+    @Test
+    public void testPrimitiveLongNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("long").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+
+    @Test
+    public void testShort() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Short").request().post(Entity.entity((short) 5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Short((short) 6), response.readEntity(Short.class));
+    }
+
+    @Test
+    public void testPrimitiveShort() {
+        WebTarget web = target().path("test");
+        Response response = web.path("short").request().post(Entity.entity((short) 5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals((short) 6, (short) response.readEntity(short.class));
+    }
+
+    @Test
+    public void testPrimitiveShortNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("short").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testByte() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Byte").request().post(Entity.entity((byte) 5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Byte((byte) 6), response.readEntity(Byte.class));
+    }
+
+    @Test
+    public void testPrimitiveByte() {
+        WebTarget web = target().path("test");
+        Response response = web.path("byte").request().post(Entity.entity((byte) 5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals((byte) 6, (byte) response.readEntity(byte.class));
+    }
+
+    @Test
+    public void testPrimitiveByteNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("byte").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testFloat() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Float").request().post(Entity.entity((float) 5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Float(6), response.readEntity(Float.class));
+    }
+
+    @Test
+    public void testPrimitiveFloat() {
+        WebTarget web = target().path("test");
+        Response response = web.path("float").request().post(Entity.entity(5f, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(6f, response.readEntity(float.class), 0.0f);
+    }
+
+    @Test
+    public void testPrimitiveFloatNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("float").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testDouble() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Double").request().post(Entity.entity((double) 5, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Double(6), response.readEntity(Double.class));
+    }
+
+    @Test
+    public void testPrimitiveDouble() {
+        WebTarget web = target().path("test");
+        Response response = web.path("double").request().post(Entity.entity(5d, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(6d, response.readEntity(double.class), 0.0d);
+    }
+
+    @Test
+    public void testPrimitiveDoubleNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("double").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testCharacter() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Character").request().post(Entity.entity('a', MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new Character('b'), response.readEntity(Character.class));
+    }
+
+    @Test
+    public void testPrimitiveCharacter() {
+        WebTarget web = target().path("test");
+        Response response = web.path("char").request().post(Entity.entity('a', MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals('b', (char) response.readEntity(char.class));
+    }
+
+    @Test
+    public void testPrimitiveCharacterNull() {
+        WebTarget web = target().path("test");
+        Response response = web.path("char").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testString() {
+        WebTarget web = target().path("test");
+        Response response = web.path("String").request().post(Entity.entity("String", MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals("StringPOST", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testBoolean() {
+        WebTarget web = target().path("test");
+        Response response = web.path("Boolean").request().post(Entity.entity(Boolean.TRUE, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(Boolean.FALSE, response.readEntity(Boolean.class));
+    }
+
+
+    @Test
+    public void testPrimitiveBoolean() {
+        WebTarget web = target().path("test");
+        Response response = web.path("boolean").request().post(Entity.entity(true, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertFalse(response.readEntity(boolean.class));
+    }
+
+    @Test
+    public void testBigDecimal() {
+        WebTarget web = target().path("test");
+        Response response = web.path("bigDecimal").request().post(Entity.entity(new BigDecimal("15"), MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new BigDecimal("16"), response.readEntity(BigDecimal.class));
+    }
+
+    @Test
+    public void testBigInteger() {
+        WebTarget web = target().path("test");
+        Response response = web.path("bigInteger").request().post(Entity.entity(new BigInteger("15"), MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(new BigInteger("16"), response.readEntity(BigInteger.class));
+    }
+
+    @Test
+    public void testAtomicInteger() {
+        WebTarget web = target().path("test");
+        Response response = web.path("atomicInteger").request().post(Entity.entity(new AtomicInteger(15),
+                MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(16, response.readEntity(AtomicInteger.class).get());
+    }
+
+    @Test
+    public void testAtomicLong() {
+        WebTarget web = target().path("test");
+        Response response = web.path("atomicLong").request().post(Entity.entity(new AtomicLong(15),
+                MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(16, response.readEntity(AtomicLong.class).get());
+    }
+
+
+    @Path("test")
+    public static class Resource {
+        @GET
+        @Path("testik")
+        public String test(@QueryParam("id") int id) {
+            System.out.println(id);
+            return String.valueOf(id);
+        }
+
+        @POST
+        @Path("Integer")
+        public Integer postInteger(Integer i) {
+            return i + 1;
+        }
+
+        @POST
+        @Path("int")
+        public int postInt(int i) {
+            return i + 1;
+        }
+
+        @POST
+        @Path("Long")
+        public Long postLong(Long l) {
+            return l + 1;
+        }
+
+        @POST
+        @Path("long")
+        public long postLong(long l) {
+            return l + 1;
+        }
+
+        @POST
+        @Path("Short")
+        public long postShort(Short s) {
+            return s + 1;
+        }
+
+
+        @POST
+        @Path("short")
+        public long postPrimitiveShort(short s) {
+            return s + 1;
+        }
+
+
+        @POST
+        @Path("Byte")
+        public Byte postByte(Byte b) {
+            return (byte) (b + 1);
+        }
+
+        @POST
+        @Path("byte")
+        public byte postPrimitiveByte(byte b) {
+            return (byte) (b + 1);
+        }
+
+
+        @POST
+        @Path("Float")
+        public Float postFloat(Float f) {
+            return f + 1f;
+        }
+
+
+        @POST
+        @Path("float")
+        public float postPrimitiveFloat(float f) {
+            return f + 1;
+        }
+
+        @POST
+        @Path("Double")
+        public Double postDouble(Double d) {
+            return d + 1d;
+        }
+
+
+        @POST
+        @Path("double")
+        public double postPrimitiveDouble(double d) {
+            return d + 1;
+        }
+
+
+        @POST
+        @Path("Character")
+        public Character postCharacter(Character c) {
+            byte b = (byte) (c.charValue());
+            b = (byte) (b + 1);
+            return (char) b;
+        }
+
+        @POST
+        @Path("char")
+        public char postPrimitiveChar(char c) {
+            byte b = (byte) c;
+            b = (byte) (b + 1);
+            return (char) b;
+        }
+
+        @POST
+        @Path("String")
+        public String postString(String str) {
+            return str + "POST";
+        }
+
+        @POST
+        @Path("Boolean")
+        public Boolean postBoolean(Boolean b) {
+            boolean bool = b;
+            return !bool;
+        }
+
+        @POST
+        @Path("boolean")
+        public Boolean postPrimitiveBoolean(boolean b) {
+            return !b;
+        }
+
+        @Path("bigDecimal")
+        @POST
+        @Consumes(MediaType.TEXT_PLAIN)
+        @Produces(MediaType.TEXT_PLAIN)
+        public BigDecimal number(BigDecimal number) {
+            return number.add(new BigDecimal("1"));
+        }
+
+
+        @Path("bigInteger")
+        @POST
+        @Consumes(MediaType.TEXT_PLAIN)
+        @Produces(MediaType.TEXT_PLAIN)
+        public BigInteger bigInteger(BigInteger bigInteger) {
+            return bigInteger.add(new BigInteger("1"));
+        }
+
+        @Path("atomicInteger")
+        @POST
+        @Consumes(MediaType.TEXT_PLAIN)
+        @Produces(MediaType.TEXT_PLAIN)
+        public AtomicInteger atomicInteger(AtomicInteger atomic) {
+            atomic.incrementAndGet();
+            return atomic;
+        }
+
+        @Path("atomicLong")
+        @POST
+        @Consumes(MediaType.TEXT_PLAIN)
+        @Produces(MediaType.TEXT_PLAIN)
+        public AtomicLong atomicLong(AtomicLong atomic) {
+            atomic.incrementAndGet();
+            return atomic;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ReloadTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ReloadTest.java
new file mode 100644
index 0000000..c8d4f6f
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ReloadTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reload capability test.
+ *
+ * For jdk http server test container, run with:
+ *
+ * mvn -Dtest=ReloadTest -Djersey.config.test.container.factory=org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory clean test
+ *
+ * For simple test container, run with:
+ *
+ * mvn -Dtest=ReloadTest -Djersey.config.test.container.factory=org.glassfish.jersey.test.simple.SimpleTestContainerFactory clean test
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ReloadTest extends JerseyTest {
+
+    @Path("one")
+    public static class One {
+        @GET
+        public String get() {
+            return "one";
+        }
+    }
+
+    @Path("two")
+    public static class Two {
+        @GET
+        public String get() {
+            return "two";
+        }
+    }
+
+    private static class Reloader extends AbstractContainerLifecycleListener {
+        Container container;
+
+
+        public void reload(ResourceConfig rc) {
+            container.reload(rc);
+        }
+
+        @Override
+        public void onStartup(Container container) {
+            this.container = container;
+        }
+    }
+
+    ResourceConfig rc;
+    Reloader reloader;
+
+    private ResourceConfig _createRC(Reloader r) {
+        final ResourceConfig result = new ResourceConfig(One.class);
+        result.registerInstances(r);
+
+        return result;
+    }
+
+    @Override
+    public ResourceConfig configure() {
+        reloader = new Reloader();
+        return rc = _createRC(reloader);
+    }
+
+
+    @Test
+    public void testReload() {
+
+        assertEquals("one", target().path("one").request().get().readEntity(String.class));
+        assertEquals(404, target().path("two").request().get().getStatus());
+
+        rc = _createRC(reloader).registerClasses(Two.class);
+        reloader.reload(rc);
+
+        assertEquals("one", target().path("one").request().get().readEntity(String.class));
+        assertEquals("two", target().path("two").request().get().readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/RequestScopedAndAsyncTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/RequestScopedAndAsyncTest.java
new file mode 100644
index 0000000..30e4372
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/RequestScopedAndAsyncTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015, 2018 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.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.DisposableSupplier;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * JERSEY-2677 reproducer - test, that {@code Factory.dispose()} is correctly called for both sync and async cases.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class RequestScopedAndAsyncTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(RequestScopedAndAsyncTest.class.getName());
+
+    // latch to prevent, that the balance is checked before dispose() is called
+    private static CountDownLatch cdl;
+
+    public static class Injectable {
+        private String message = "Hello";
+        public String getMessage() {
+            return message;
+        }
+    }
+
+    public static class InjectableFactory implements DisposableSupplier<Injectable> {
+        private static AtomicInteger provided = new AtomicInteger(0);
+        private static AtomicInteger balance = new AtomicInteger(0);
+
+        @Override
+        public Injectable get() {
+            LOGGER.fine("Factory provide() called.");
+            provided.incrementAndGet();
+            balance.incrementAndGet();
+            return new Injectable();
+        }
+
+        @Override
+        public void dispose(Injectable i) {
+            LOGGER.fine("Factory dispose() called. ");
+            balance.decrementAndGet();
+            cdl.countDown();
+        }
+
+        public static void reset() {
+            LOGGER.fine("Factory reset() called.");
+            provided.set(0);
+            balance.set(0);
+            cdl = new CountDownLatch(1);
+        }
+
+        public static int getProvidedCount() {
+            return provided.intValue();
+        }
+
+        public static int getBalanceValue() {
+            return balance.intValue();
+        }
+    }
+
+    @Path("test")
+    public static class TestResource {
+
+        @Inject
+        private Injectable injectable;
+
+        @GET
+        @Path("sync")
+        public Response sync() {
+            LOGGER.fine("Injected message: " + injectable.getMessage());
+            return Response.noContent().build();
+        }
+
+        @GET
+        @Path("async")
+        public void async(@Suspended AsyncResponse ar) {
+            LOGGER.fine("Injected message: " + injectable.getMessage());
+            ar.resume(Response.noContent().build());
+        }
+    }
+
+    @Before
+    public void resetCounters() {
+        InjectableFactory.reset();
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class)
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bindFactory(InjectableFactory.class)
+                                .to(Injectable.class)
+                                .in(RequestScoped.class);
+                    }
+                });
+    }
+
+    @Test
+    public void testInstanceReleaseAsync() throws ExecutionException, InterruptedException {
+        Future<Response> future = target("/test/async").request().async().get();
+        Response response = future.get();
+
+        assertEquals(204, response.getStatus());
+        assertEquals(1, InjectableFactory.getProvidedCount());
+        try {
+            cdl.await(500, TimeUnit.MILLISECONDS);
+        } catch (final InterruptedException e) {
+            LOGGER.log(Level.INFO, "CountDownLatch interrupted: ", e);
+        }
+        assertEquals(0, InjectableFactory.getBalanceValue());
+    }
+
+    @Test
+    public void testInstanceReleaseSync() {
+        assertEquals(204, target("/test/sync").request().get().getStatus());
+        assertEquals(1, InjectableFactory.getProvidedCount());
+        assertEquals(0, InjectableFactory.getBalanceValue());
+    }
+
+    @Test
+    public void shouldProvideAndDisposeSync2() {
+        assertEquals(204, target("/test/sync").request().get().getStatus());
+        assertEquals(1, InjectableFactory.getProvidedCount());
+        assertEquals(0, InjectableFactory.getBalanceValue());
+    }
+
+    @Test
+    public void shouldProvideAndDisposeAsync2() throws ExecutionException, InterruptedException {
+        assertEquals(204, target("/test/async").request().get().getStatus());
+        assertEquals(1, InjectableFactory.getProvidedCount());
+        try {
+            cdl.await(500, TimeUnit.MILLISECONDS);
+        } catch (final InterruptedException e) {
+            LOGGER.log(Level.INFO, "CountDownLatch interrupted: ", e);
+        }
+        assertEquals(0, InjectableFactory.getBalanceValue());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceConfigTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceConfigTest.java
new file mode 100644
index 0000000..f6f4f04
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceConfigTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2012, 2018 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.util.Formatter;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * End to end test class for testing {@code ResourceConfig} features.
+ *
+ * @author Michal Gajdos
+ */
+public class ResourceConfigTest extends JerseyTest {
+
+    @SuppressWarnings({"UnusedDeclaration", "StringEquality", "RedundantIfStatement"})
+    @XmlRootElement
+    public static class Property {
+
+        private String name;
+        private String value;
+
+        public Property() {
+        }
+
+        public Property(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Property)) {
+                return false;
+            }
+            final Property other = (Property) obj;
+            if (this.name != other.name && (this.name == null || !this.name.equals(other.name))) {
+                return false;
+            }
+            if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = 7;
+            hash = hash * 47 + (name == null ? 0 : name.hashCode());
+            hash = hash * 47 + (value == null ? 0 : value.hashCode());
+            return hash;
+        }
+
+        @Override
+        public String toString() {
+            return new Formatter().format("Property(name=%s,value=%s)", name, value).toString();
+        }
+
+    }
+
+    @Path("/")
+    @Produces("application/json")
+    @Consumes("application/json")
+    public static class Resource {
+
+        @POST
+        public Property post(final Property property) {
+            return property;
+        }
+
+    }
+
+    /**
+     * Application similar to the one that needs to register a custom {@code Feature} to run properly
+     * and is supposed to be present in WAR files.
+     */
+    public static class Jersey1094 extends ResourceConfig {
+
+        public Jersey1094() {
+            registerClasses(Resource.class, JettisonFeature.class);
+        }
+
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        // Simulate the creation of the ResourceConfig as if it was created during servlet initialization
+        return ResourceConfig.forApplicationClass(Jersey1094.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new JettisonFeature());
+    }
+
+    /**
+     * Tests whether the {@code ApplicationHandler} is able to register and use custom binders provided by an extension of
+     * {@code ResourceConfig} if only a class reference of this extension is passed in the {@code ResourceConfig} to the
+     * {@code ApplicationHandler}.
+     * <p/>
+     * Test is trying to simulate the behaviour of Jersey as if the application was deployed into a servlet container
+     * with a servlet defined in {@code web.xml} file using {@code init-param} {@code javax.ws.rs.Application}.
+     */
+    @Test
+    public void testJersey1094() throws Exception {
+        Property testInstance = new Property("myProp", "myVal");
+
+        final Property returnedValue = target()
+                .path("/")
+                .request("application/json")
+                .post(Entity.entity(testInstance, "application/json"), Property.class);
+
+        assertEquals(testInstance, returnedValue);
+    }
+
+    @Test
+    @Ignore("TODO: Add test for reloading resource config in the container (once it is supported)")
+    public void testJersey1094ReloadResourceConfig() throws Exception {
+        // TODO test reloading resource config in the container (once it is supported)
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceFilterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceFilterTest.java
new file mode 100644
index 0000000..7cb4a45
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceFilterTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.Principal;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NameBinding;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * JAX-RS name-bound filter tests.
+ *
+ * @author Martin Matula
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Miroslav.Fuksa
+ * @see GloballyNameBoundResourceFilterTest
+ */
+public class ResourceFilterTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                MyDynamicFeature.class,
+                BasicTestsResource.class,
+                NameBoundRequestFilter.class,
+                NameBoundResponseFilter.class,
+                PostMatchingFilter.class,
+                // exception tests
+                ExceptionTestsResource.class,
+                ExceptionPreMatchRequestFilter.class,
+                ExceptionPostMatchRequestFilter.class,
+                ExceptionTestBoundResponseFilter.class,
+                ExceptionTestGlobalResponseFilter.class,
+                TestExceptionMapper.class,
+                AbortResponseResource.class,
+                AbortResponseFitler.class,
+                AbortRequestFilter.class,
+                ExceptionRequestFilter.class
+        );
+    }
+
+    @Test
+    public void testDynamicBinder() {
+        test("dynamicBinder");
+    }
+
+    @Test
+    public void testNameBoundRequest() {
+        test("nameBoundRequest");
+    }
+
+    @Test
+    public void testNameBoundResponse() {
+        test("nameBoundResponse");
+    }
+
+    @Test
+    public void testPostMatching() {
+        test("postMatching");
+    }
+
+    // See JERSEY-1554
+    @Test
+    public void testGlobalPostMatchingNotInvokedOn404() {
+        Response r = target("basic").path("not-found").request().get();
+        assertEquals(404, r.getStatus());
+        if (r.hasEntity()) {
+            assertThat(r.readEntity(String.class), not(containsString("postMatching")));
+        }
+    }
+
+    private void test(String name) {
+        Response r = target("basic").path(name).request().get();
+        assertEquals("Unexpected HTTP response status code.", 200, r.getStatus());
+        assertTrue("Response does not have entity.", r.hasEntity());
+        assertEquals("Unexpected response entity value.", name, r.readEntity(String.class));
+    }
+
+    @Path("/basic")
+    public static class BasicTestsResource {
+
+        @Path("dynamicBinder")
+        @GET
+        public String getDynamicBinder() {
+            return "";
+        }
+
+        @Path("nameBoundRequest")
+        @GET
+        @NameBoundRequest
+        public String getNameBoundRequest() {
+            return "";
+        }
+
+        @Path("nameBoundResponse")
+        @GET
+        @NameBoundResponse
+        public String getNameBoundResponse() {
+            return "";
+        }
+
+        @Path("postMatching")
+        @GET
+        public String getPostMatching() {
+            return "";
+        }
+    }
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface NameBoundRequest {}
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface NameBoundAbortResponse {}
+
+    @NameBoundRequest
+    @Priority(1)
+    public static class NameBoundRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok("nameBoundRequest", MediaType.TEXT_PLAIN_TYPE).build());
+        }
+    }
+
+    @NameBoundResponse
+    public static class NameBoundResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            responseContext.setEntity("nameBoundResponse", responseContext.getEntityAnnotations(), MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+
+    public static class PostMatchingFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if (requestContext.getUriInfo().getPath().startsWith("basic")) {
+                requestContext.abortWith(Response.ok("postMatching", MediaType.TEXT_PLAIN_TYPE).build());
+            }
+        }
+    }
+
+    @Priority(1)
+    @PreMatching
+    private static class DbFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok("dynamicBinder", MediaType.TEXT_PLAIN_TYPE).build());
+        }
+    }
+
+    public static class MyDynamicFeature implements DynamicFeature {
+
+        private final DbFilter filter = new DbFilter();
+
+        @Override
+        public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
+            if ("getDynamicBinder".equals(resourceInfo.getResourceMethod().getName())) {
+                context.register(filter);
+            }
+        }
+    }
+
+    @Path("/exception")
+    public static class ExceptionTestsResource {
+
+        @Path("matched")
+        @GET
+        @ExceptionTestBound
+        public String getPostMatching() {
+            return "method";
+        }
+    }
+
+    @PreMatching
+    public static class ExceptionPreMatchRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if ("exception/not-found".equals(requestContext.getUriInfo().getPath())) {
+                throw new TestException("globalRequest");
+            }
+        }
+    }
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface ExceptionTestBound {}
+
+    @ExceptionTestBound
+    public static class ExceptionPostMatchRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            throw new TestException("nameBoundRequest");
+        }
+    }
+
+    @ExceptionTestBound
+    @Priority(10)
+    public static class ExceptionTestBoundResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            responseContext.setEntity(
+                    (responseContext.hasEntity()) ? responseContext.getEntity() + "-nameBoundResponse" : "nameBoundResponse",
+                    responseContext.getEntityAnnotations(),
+                    MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+
+    @Priority(1)
+    public static class ExceptionTestGlobalResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            if (!requestContext.getUriInfo().getPath().startsWith("exception")) {
+                return;
+            }
+
+            responseContext.setEntity(
+                    (responseContext.hasEntity()) ? responseContext.getEntity() + "-globalResponse" : "globalResponse",
+                    responseContext.getEntityAnnotations(),
+                    MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+
+    public static class TestException extends RuntimeException {
+
+        public TestException(String message) {
+            super(message);
+        }
+    }
+
+    public static class TestExceptionMapper implements ExceptionMapper<TestException> {
+
+        public static final String POSTFIX = "-mappedTestException";
+
+        @Override
+        public Response toResponse(TestException exception) {
+            return Response.ok(exception.getMessage() + POSTFIX).build();
+        }
+    }
+
+    @Test
+    public void testNameBoundResponseFilterNotInvokedOnPreMatchFilterException() {
+        Response r = target("exception").path("not-found").request().get();
+        assertEquals(200, r.getStatus());
+        assertTrue(r.hasEntity());
+        assertEquals("globalRequest-mappedTestException-globalResponse", r.readEntity(String.class));
+    }
+
+    @Test
+    public void testNameBoundResponseFilterInvokedOnPostMatchFilterException() {
+        Response r = target("exception").path("matched").request().get();
+        assertEquals(200, r.getStatus());
+        assertTrue(r.hasEntity());
+        assertEquals("nameBoundRequest-mappedTestException-nameBoundResponse-globalResponse", r.readEntity(String.class));
+    }
+
+    @NameBoundAbortResponse
+    private static class AbortResponseFitler implements ContainerResponseFilter {
+
+        public static final String ABORT_FILTER_TEST_PASSED = "abort-filter-test-passed";
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            boolean passed = true;
+
+            try {
+                // checks that IllegalStateException is thrown when setting entity stream
+                requestContext.setEntityStream(new InputStream() {
+                    @Override
+                    public int read() throws IOException {
+                        return -1;
+                    }
+                });
+                passed = false;
+            } catch (IllegalStateException iae) {
+                System.out.println(iae.getMessage());
+            }
+
+            try {
+                // checks that IllegalStateException is thrown when setting security context
+                requestContext.setSecurityContext(new SecurityContext() {
+                    @Override
+                    public Principal getUserPrincipal() {
+                        return null;
+                    }
+
+                    @Override
+                    public boolean isUserInRole(String role) {
+                        return false;
+                    }
+
+                    @Override
+                    public boolean isSecure() {
+                        return false;
+                    }
+
+                    @Override
+                    public String getAuthenticationScheme() {
+                        return null;
+                    }
+                });
+                passed = false;
+            } catch (IllegalStateException iae) {
+                System.out.println(iae.getMessage());
+            }
+
+            try {
+                // checks that IllegalStateException is thrown when aborting request
+                requestContext.abortWith(Response.serverError().build());
+                passed = false;
+            } catch (IllegalStateException iae) {
+                System.out.println(iae.getMessage());
+            }
+            responseContext.getHeaders().add(ABORT_FILTER_TEST_PASSED, passed);
+        }
+    }
+
+    @NameBoundAbortRequest
+    private static class AbortRequestFilter implements ContainerRequestFilter {
+
+        public static final String FILTER_ABORT_ENTITY = "filter-abort-entity";
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.abortWith(Response.ok(FILTER_ABORT_ENTITY).build());
+        }
+    }
+
+    @NameBoundExceptionInRequest
+    private static class ExceptionRequestFilter implements ContainerRequestFilter {
+
+        public static final String EXCEPTION_IN_REQUEST_FILTER = "exception-in-request-filter";
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            throw new TestException(EXCEPTION_IN_REQUEST_FILTER);
+        }
+    }
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface NameBoundResponse {}
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface NameBoundAbortRequest {}
+
+    @NameBinding
+    @Retention(RetentionPolicy.RUNTIME)
+    private static @interface NameBoundExceptionInRequest {}
+
+    @Path("abort")
+    public static class AbortResponseResource {
+
+        @Path("response")
+        @GET
+        @NameBoundAbortResponse
+        public String get() {
+            return "get";
+        }
+
+        @Path("abort-in-filter")
+        @GET
+        @NameBoundAbortResponse
+        @NameBoundAbortRequest
+        public String neverCalled() {
+            return "This method will never be called. Request will be aborted in a request filter.";
+        }
+
+        @Path("exception")
+        @GET
+        @NameBoundAbortResponse
+        @NameBoundExceptionInRequest
+        public String exception() {
+            return "This method will never be called. Exception will be thrown in a request filter.";
+        }
+    }
+
+    @Test
+    public void testAbortResponseInResponseFilter() {
+        Response r = target("abort").path("response").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("get", r.readEntity(String.class));
+        assertNotNull(r.getHeaderString(AbortResponseFitler.ABORT_FILTER_TEST_PASSED));
+        assertEquals("true", r.getHeaderString(AbortResponseFitler.ABORT_FILTER_TEST_PASSED));
+    }
+
+    @Test
+    public void testAbortAbortedResponseInResponseFilter() {
+        Response r = target("abort").path("abort-in-filter").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals(AbortRequestFilter.FILTER_ABORT_ENTITY, r.readEntity(String.class));
+        assertNotNull(r.getHeaderString(AbortResponseFitler.ABORT_FILTER_TEST_PASSED));
+        assertEquals("true", r.getHeaderString(AbortResponseFitler.ABORT_FILTER_TEST_PASSED));
+    }
+
+    @Test
+    public void testAbortedResponseFromExceptionResponse() {
+        Response r = target("abort").path("exception").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals(ExceptionRequestFilter.EXCEPTION_IN_REQUEST_FILTER + TestExceptionMapper.POSTFIX,
+                r.readEntity(String.class));
+        assertNotNull(r.getHeaderString(AbortResponseFitler.ABORT_FILTER_TEST_PASSED));
+        assertEquals("true", r.getHeaderString(AbortResponseFitler.ABORT_FILTER_TEST_PASSED));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceRoutingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceRoutingTest.java
new file mode 100644
index 0000000..6274c4a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceRoutingTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/**
+ * @author Miroslav Fuksa
+ */
+public class ResourceRoutingTest extends JerseyTest {
+
+    @Path("a")
+    public static class ResourceA {
+        @Path("b/d")
+        @GET
+        public String get() {
+            // this method cannot be chosen as the request path "a/b/..." always firstly choose the ResourceAB and routing
+            // will never check this resource. This is based on the algorithm from the jax-rs spec 2
+            return "a/b/d";
+        }
+
+        @Path("q")
+        @GET
+        public String getQ() {
+            return "a/q";
+        }
+
+    }
+
+    @Path("a/b")
+    public static class ResourceAB {
+        @Path("c")
+        @GET
+        public String get() {
+            return "a/b/c";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ResourceA.class, ResourceAB.class);
+    }
+
+
+    @Test
+    public void subWrongPath() throws Exception {
+        Response response = target("a/b/d").request().get();
+        Assert.assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    public void correctPath() throws Exception {
+        Response response = target("a/b/c").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("a/b/c", response.readEntity(String.class));
+    }
+
+    @Test
+    public void correctPath2() throws Exception {
+        Response response = target("a/q").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("a/q", response.readEntity(String.class));
+    }
+
+}
+
+
+
+
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResponseStatusTypeTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResponseStatusTypeTest.java
new file mode 100644
index 0000000..7c59ae8
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ResponseStatusTypeTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2013, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Tests custom response status reason phrase with jersey containers and connectors.
+ *
+ * @author Miroslav Fuksa
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({ResponseStatusTypeTest.InMemoryTest.class,
+        ResponseStatusTypeTest.GrizzlyContainerGrizzlyConnectorTest.class,
+        ResponseStatusTypeTest.GrizzlyContainerApacheConnectorTest.class,
+        ResponseStatusTypeTest.SimpleContainerHttpUrlConnectorTest.class})
+public class ResponseStatusTypeTest {
+
+    public static final String REASON_PHRASE = "my-phrase";
+
+    public static class InMemoryTest extends JerseyTest {
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(TestResource.class);
+
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new InMemoryTestContainerFactory();
+        }
+
+        @Test
+        public void testCustom() {
+            _testCustom(target());
+        }
+
+        @Test
+        public void testBadRequest() {
+            _testBadRequest(target());
+        }
+
+        @Test
+        public void testCustomBadRequest() {
+            // with InMemory container and connector status info should be transferred as it is produced.
+            final Response response = target().path("resource/custom-bad-request").request().get();
+            Assert.assertEquals(400, response.getStatus());
+            Assert.assertNull(response.getStatusInfo().getReasonPhrase());
+
+        }
+
+    }
+
+    public static class GrizzlyContainerGrizzlyConnectorTest extends JerseyTest {
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(TestResource.class);
+
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new GrizzlyTestContainerFactory();
+        }
+
+        @Override
+        protected void configureClient(ClientConfig config) {
+            config.connectorProvider(new GrizzlyConnectorProvider());
+        }
+
+
+        @Test
+        public void testCustom() {
+            _testCustom(target());
+        }
+
+        @Test
+        public void testBadRequest() {
+            _testBadRequest(target());
+        }
+
+        @Test
+        public void testCustomBadRequest() {
+            _testCustomBadRequest(target());
+        }
+    }
+
+    public static class GrizzlyContainerApacheConnectorTest extends JerseyTest {
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(TestResource.class);
+
+        }
+
+        @Override
+        protected void configureClient(ClientConfig config) {
+            config.connectorProvider(new ApacheConnectorProvider());
+        }
+
+
+        @Test
+        public void testCustom() {
+            _testCustom(target());
+        }
+
+        @Test
+        public void testBadRequest() {
+            _testBadRequest(target());
+        }
+
+        @Test
+        public void testCustomBadRequest() {
+            _testCustomBadRequest(target());
+        }
+    }
+
+    public static class SimpleContainerHttpUrlConnectorTest extends JerseyTest {
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(TestResource.class);
+
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new SimpleTestContainerFactory();
+        }
+
+        @Test
+        public void testCustom() {
+            _testCustom(target());
+        }
+
+        @Test
+        public void testBadRequest() {
+            _testBadRequest(target());
+        }
+
+        @Test
+        public void testCustomBadRequest() {
+            _testCustomBadRequest(target());
+        }
+    }
+
+    public static class JdkHttpContainerHttpUrlConnectorTest extends JerseyTest {
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(TestResource.class);
+
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new JdkHttpServerTestContainerFactory();
+        }
+
+        @Test
+        @Ignore("Jdk http container does not support custom response reason phrases.")
+        public void testCustom() {
+            _testCustom(target());
+        }
+
+        @Test
+        public void testBadRequest() {
+            _testBadRequest(target());
+        }
+
+        @Test
+        public void testCustomBadRequest() {
+            _testCustomBadRequest(target());
+        }
+    }
+
+    @Path("resource")
+    public static class TestResource {
+
+        @GET
+        @Path("custom")
+        public Response testStatusType() {
+            return Response.status(new Custom428Type()).build();
+        }
+
+        @GET
+        @Path("bad-request")
+        public Response badRequest() {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+
+        @GET
+        @Path("custom-bad-request")
+        public Response customBadRequest() {
+            return Response.status(new CustomBadRequestWithoutReasonType()).build();
+        }
+
+    }
+
+    public static class Custom428Type implements Response.StatusType {
+        @Override
+        public int getStatusCode() {
+            return 428;
+        }
+
+        @Override
+        public String getReasonPhrase() {
+            return REASON_PHRASE;
+        }
+
+        @Override
+        public Response.Status.Family getFamily() {
+            return Response.Status.Family.CLIENT_ERROR;
+        }
+    }
+
+    public static class CustomBadRequestWithoutReasonType implements Response.StatusType {
+        @Override
+        public int getStatusCode() {
+            return 400;
+        }
+
+        @Override
+        public String getReasonPhrase() {
+            return null;
+        }
+
+        @Override
+        public Response.Status.Family getFamily() {
+            return Response.Status.Family.CLIENT_ERROR;
+        }
+    }
+
+    public static void _testCustom(WebTarget target) {
+        final Response response = target.path("resource/custom").request().get();
+        Assert.assertEquals(428, response.getStatus());
+        Assert.assertEquals(REASON_PHRASE, response.getStatusInfo().getReasonPhrase());
+    }
+
+    public static void _testBadRequest(WebTarget target) {
+        final Response response = target.path("resource/bad-request").request().get();
+        Assert.assertEquals(400, response.getStatus());
+        Assert.assertEquals(Response.Status.BAD_REQUEST.getReasonPhrase(), response.getStatusInfo().getReasonPhrase());
+    }
+
+    public static void _testCustomBadRequest(WebTarget target) {
+        final Response response = target.path("resource/custom-bad-request").request().get();
+        Assert.assertEquals(400, response.getStatus());
+        Assert.assertEquals(Response.Status.BAD_REQUEST.getReasonPhrase(), response.getStatusInfo().getReasonPhrase());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/RuntimeConfigTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/RuntimeConfigTest.java
new file mode 100644
index 0000000..91e0d1b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/RuntimeConfigTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.internal.InternalProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class RuntimeConfigTest extends JerseyTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String get() {
+            return "get";
+        }
+    }
+
+    public static class EmptyFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            return true;
+        }
+    }
+
+    public static class ClientFeature implements Feature {
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            context.register(ClientReaderInterceptor.class);
+            context.property("foo", "bar");
+            return true;
+        }
+    }
+
+    public static class ClientReaderInterceptor implements ReaderInterceptor {
+
+        private final Configuration config;
+
+        @Inject
+        public ClientReaderInterceptor(final Configuration configuration) {
+            this.config = configuration;
+        }
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            assertTrue(config.isRegistered(ClientFeature.class));
+            assertTrue(config.isRegistered(ClientReaderInterceptor.class));
+
+            assertThat(config.getProperties().size(), is(2));
+            assertThat(config.getProperty("foo").toString(), is("bar"));
+
+            // JsonFeature
+            assertThat(config.getProperty(InternalProperties.JSON_FEATURE_CLIENT), notNullValue());
+
+            // MetaInfAutoDiscoverable
+            assertThat(config.getInstances().size(), is(1));
+            assertTrue(config.isEnabled(ClientFeature.class));
+
+            context.getHeaders().add("CustomHeader", "ClientReaderInterceptor");
+
+            return context.proceed();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testRuntimeClientConfig() throws Exception {
+        final WebTarget target = target();
+
+        target.register(ClientFeature.class);
+
+        final Response response = target.request(MediaType.WILDCARD_TYPE).get(Response.class);
+
+        assertEquals(1, target.getConfiguration().getClasses().size());
+        assertTrue(target.getConfiguration().isRegistered(ClientFeature.class));
+        assertTrue(target.getConfiguration().getInstances().isEmpty());
+        assertTrue(target.getConfiguration().getProperties().isEmpty());
+        assertFalse(target.getConfiguration().isEnabled(ClientFeature.class));
+
+        WebTarget t = target();
+        assertEquals(0, t.getConfiguration().getClasses().size());
+        assertFalse(t.getConfiguration().isRegistered(ClientFeature.class));
+        assertTrue(t.getConfiguration().getInstances().isEmpty());
+        assertTrue(t.getConfiguration().getProperties().isEmpty());
+        assertFalse(t.getConfiguration().isEnabled(ClientFeature.class));
+
+        assertEquals("get", response.readEntity(String.class));
+        assertEquals("ClientReaderInterceptor", response.getHeaderString("CustomHeader"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SecurityContextFilterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SecurityContextFilterTest.java
new file mode 100644
index 0000000..afc7d2d
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SecurityContextFilterTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2012, 2018 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.security.Principal;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * End to end test class for testing security context in the Filter and
+ * resource.
+ *
+ * @author Miroslav Fuksa
+ */
+public class SecurityContextFilterTest extends JerseyTest {
+
+    private static final String PRINCIPAL_NAME = "test_principal_setByFilter";
+    private static final String SKIP_FILTER = "skipFilter";
+    private static final String PRINCIPAL_IS_NULL = "principalIsNull";
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(SecurityContextFilter.class, Resource.class);
+    }
+
+    /**
+     * Test security context container request filter.
+     */
+    @PreMatching
+    public static class SecurityContextFilter implements ContainerRequestFilter {
+
+        // TODO: won't work until we have proxiable scope
+//        @Context
+//        SecurityContext securityContext;
+
+        @Override
+        public void filter(ContainerRequestContext context) {
+            Assert.assertNotNull(context.getSecurityContext());
+
+            // test injections
+            // TODO: won't work until SecurityContext is proxiable
+//            Assert.assertEquals(context.getSecurityContext(), securityContext);
+
+            String header = context.getHeaders().getFirst(SKIP_FILTER);
+            if ("true".equals(header)) {
+                return;
+            }
+
+            context.setSecurityContext(new SecurityContext() {
+                @Override
+                public boolean isUserInRole(String role) {
+                    return false;
+                }
+
+                @Override
+                public boolean isSecure() {
+                    return false;
+                }
+
+                @Override
+                public Principal getUserPrincipal() {
+                    return new Principal() {
+
+                        @Override
+                        public String getName() {
+                            return PRINCIPAL_NAME;
+                        }
+                    };
+                }
+
+                @Override
+                public String getAuthenticationScheme() {
+                    return null;
+                }
+            });
+        }
+    }
+
+    /**
+     * Tests SecurityContext in filter.
+     *
+     * @throws Exception Thrown when request processing fails in the
+     *                   application.
+     */
+    @Test
+    public void testSecurityContextFilter() throws Exception {
+        Response response = target().path("test").request().get();
+        assertEquals(200, response.getStatus());
+        String entity = response.readEntity(String.class);
+        assertEquals(PRINCIPAL_NAME, entity);
+    }
+
+    /**
+     * Tests SecurityContext in filter.
+     *
+     * @throws Exception Thrown when request processing fails in the
+     *                   application.
+     */
+    @Test
+    public void testContainerSecurityContext() throws Exception {
+        Response response = target().path("test").request().header(SKIP_FILTER, "true").get();
+        assertEquals(200, response.getStatus());
+        String entity = response.readEntity(String.class);
+        Assert.assertTrue(!entity.equals(PRINCIPAL_NAME));
+    }
+
+    /**
+     * Test resource class.
+     */
+    @Path("test")
+    public static class Resource {
+
+        /**
+         * Test resource method.
+         *
+         * @param crc container request context.
+         * @return String response with principal name.
+         */
+
+        // TODO: inject SecurityContext directly once JERSEY-1282 is fixed
+        @GET
+        public String getPrincipal(@Context ContainerRequestContext crc) {
+            Assert.assertNotNull(crc.getSecurityContext());
+            Principal userPrincipal = crc.getSecurityContext().getUserPrincipal();
+            return userPrincipal == null ? PRINCIPAL_IS_NULL : userPrincipal.getName();
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ServerDestroyTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ServerDestroyTest.java
new file mode 100644
index 0000000..f2a76de
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ServerDestroyTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.DisposableSupplier;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Assert that pre destroy method on application, resources and providers is invoked.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+public class ServerDestroyTest extends JerseyTest {
+
+    private static final Map<String, Boolean> destroyed = new HashMap<>();
+
+    private Reloader reloader;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        destroyed.clear();
+        destroyed.put("application", false);
+        destroyed.put("singleton-resource", false);
+        destroyed.put("filter", false);
+        destroyed.put("writer", false);
+        destroyed.put("singleton-factory", false);
+        destroyed.put("feature", false);
+
+        super.setUp();
+    }
+
+    @Path("/")
+    @Singleton
+    public static class Resource {
+
+        @GET
+        public String get() {
+            return "resource";
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("singleton-resource", true);
+        }
+    }
+
+    public static class MyFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext,
+                           final ContainerResponseContext responseContext) throws IOException {
+            responseContext.getHeaders().putSingle("foo", "bar");
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("filter", true);
+        }
+    }
+
+    public static class MyWriter implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            context.setEntity("writer-" + context.getEntity());
+            context.proceed();
+        }
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("writer", true);
+        }
+    }
+
+    public static class MyApplication extends Application {
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("application", true);
+        }
+
+        @Override
+        public Set<Class<?>> getClasses() {
+            return Arrays.asList(
+                    Resource.class,
+                    MyFilter.class,
+                    MyWriter.class,
+                    MyContainerLifecycleListener.class,
+                    MyFeature.class).stream().collect(Collectors.toSet());
+        }
+
+        @Override
+        public Set<Object> getSingletons() {
+            return Collections.singleton(new AbstractBinder() {
+                @Override
+                protected void configure() {
+                    bindFactory(SingletonFactory.class)
+                            .to(SingletonInstance.class)
+                            .in(Singleton.class);
+                }
+            });
+        }
+    }
+
+    public static class SingletonInstance {
+
+        public void dispose() {
+            destroyed.put("singleton-factory", true);
+        }
+    }
+
+    public static class SingletonFactory implements DisposableSupplier<SingletonInstance> {
+
+        @Override
+        public SingletonInstance get() {
+            return new SingletonInstance();
+        }
+
+        @Override
+        public void dispose(final SingletonInstance instance) {
+            instance.dispose();
+        }
+    }
+
+    private static class Reloader extends AbstractContainerLifecycleListener {
+
+        Container container;
+
+        public void reload(final ResourceConfig config) {
+            container.reload(config);
+        }
+
+        @Override
+        public void onStartup(final Container container) {
+            this.container = container;
+        }
+    }
+
+    public static class MyContainerLifecycleListener extends AbstractContainerLifecycleListener {
+
+        @Inject
+        private SingletonInstance instance;
+
+        @Override
+        public void onShutdown(final Container container) {
+            assertThat(instance, notNullValue());
+        }
+    }
+
+    public static class MyFeature implements Feature {
+
+        @PreDestroy
+        public void preDestroy() {
+            destroyed.put("feature", true);
+        }
+
+        @Override
+        public boolean configure(final FeatureContext context) {
+            return true;
+        }
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        reloader = new Reloader();
+
+        return DeploymentContext.newInstance(ResourceConfig.forApplicationClass(MyApplication.class).register(reloader));
+    }
+
+    @Test
+    public void testApplicationResource() throws Exception {
+        final Response response = target().request().get();
+        assertThat(response.readEntity(String.class), is("writer-resource"));
+        assertThat(response.getStringHeaders().getFirst("foo"), is("bar"));
+
+        checkDestroyed(false);
+        reloader.reload(new ResourceConfig(Resource.class));
+        checkDestroyed(true);
+    }
+
+    private void checkDestroyed(final boolean shouldBeDestroyed) {
+        for (final Map.Entry<String, Boolean> entry : destroyed.entrySet()) {
+            assertThat(entry.getKey() +  " should" + (shouldBeDestroyed ? "" : " not") + " be destroyed",
+                    entry.getValue(), is(shouldBeDestroyed));
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProviderTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProviderTest.java
new file mode 100644
index 0000000..16ee4a1
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProviderTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.spi.ContextResolvers;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Class testing that providers are managed correctly in the singleton scope.
+ *
+ * @author Miroslav Fuksa
+ */
+public class SingletonProviderTest extends JerseyTest {
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(SingletonResource.class, AnyFilter.class,
+                ExceptionMappingProvider.class, SingletonStringMessageProvider.class, MyContextResolver.class);
+    }
+
+    @Test
+    public void filterTest() {
+        // NOTE: Explicit acceptedMedia type definition is not needed for HttpUrlConnector, as it sends several "default" types
+        // when not set directly. For (some) other connectors (namely JdkConnector), this is required, as the inferred accepted
+        // media type is */*, which would match the provider, that produces text/test, which is what we don't want in this case.
+        String str;
+        str = target().path("resource/filter").request("text/plain").get().readEntity(String.class);
+        assertEquals("filter:1", str);
+
+        str = target().path("resource/filter").request("text/plain").get().readEntity(String.class);
+        assertEquals("filter:2", str);
+    }
+
+
+    @Test
+    public void exceptionMapperTest() {
+        // NOTE: Explicit acceptedMedia type definition is not needed for HttpUrlConnector, as it sends several "default" types
+        // when not set directly. For (some) other connectors (namely JdkConnector), this is required, as the inferred accepted
+        // media type is */*, which would match the provider, that produces text/test, which is what we don't want in this case.
+        String str;
+        str = target().path("resource/exception").request("text/plain").get().readEntity(String.class);
+        assertEquals("mapper:1", str);
+        str = target().path("resource/exception").request("text/plain").get().readEntity(String.class);
+        assertEquals("mapper:2", str);
+
+    }
+
+    @Test
+    public void messageBodyWriterTest() {
+        String str1;
+        str1 = target().path("resource/messagebody").request("text/test").get().readEntity(String.class);
+        assertTrue(str1.endsWith(":1"));
+        String str2;
+        str2 = target().path("resource/messagebody").request("text/test").get().readEntity(String.class);
+        assertTrue(str2.endsWith(":2"));
+
+        assertEquals(str1.substring(0, str1.length() - 2), str2.substring(0, str2.length() - 2));
+    }
+
+    @Test
+    public void messageBodyReaderTest() {
+        String str1 = target().path("resource/messagebodyreader").request("text/plain")
+                .put(Entity.entity("from-client", "text/test")).readEntity(String.class);
+        assertTrue(str1.endsWith(":1"));
+        String str2 = target().path("resource/messagebodyreader").request("text/plain").put(Entity.entity("from-client",
+                "text/test")).readEntity(String.class);
+        assertTrue(str2.endsWith(":2"));
+
+        assertEquals(str1.substring(0, str1.length() - 2), str2.substring(0, str2.length() - 2));
+    }
+
+    @Test
+    public void contextResolverTest() {
+        String str1 = target().path("resource/context").request("text/plain").get().readEntity(String.class);
+        assertEquals("context:1", str1);
+
+        String str2 = target().path("resource/context").request("text/plain").get().readEntity(String.class);
+        assertEquals("context:2", str2);
+    }
+
+    @Path("resource")
+    public static class SingletonResource {
+        @GET
+        @Path("filter")
+        public String getCounterFromFilter(@HeaderParam("counter") int counter) {
+            return "filter:" + counter;
+        }
+
+        @GET
+        @Path("exception")
+        public String throwException() {
+            throw new SingletonTestException("test exception");
+        }
+
+        @GET
+        @Path("messagebody")
+        @Produces("text/test")
+        public String messageBodyTest() {
+            return "messagebody:";
+        }
+
+        @PUT
+        @Path("messagebodyreader")
+        @Produces("text/plain")
+        @Consumes("text/test")
+        public String messageBodyReaderTest(String entity) {
+            return "put:" + entity;
+        }
+
+        @GET
+        @Path("context")
+        @Produces(MediaType.TEXT_PLAIN)
+        public String testContextResolver(@Context ContextResolvers resolvers) {
+            ContextResolver<String> contextResolver = resolvers.resolve(String.class, MediaType.TEXT_PLAIN_TYPE);
+            String context = contextResolver.getContext(String.class);
+            return context;
+        }
+    }
+
+
+    public static class AnyFilter implements ContainerRequestFilter {
+        private int counter = 1;
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.getHeaders().add("counter", String.valueOf(counter++));
+        }
+    }
+
+
+    @Provider
+    public static class ExceptionMappingProvider implements ExceptionMapper<SingletonTestException> {
+        private int counter = 1;
+
+        @Override
+        public Response toResponse(SingletonTestException exception) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("mapper:" + counter++).build();
+        }
+    }
+
+    public static class SingletonTestException extends RuntimeException {
+        public SingletonTestException() {
+            super();
+        }
+
+        public SingletonTestException(String message) {
+            super(message);
+        }
+    }
+
+    @Produces(MediaType.TEXT_PLAIN)
+    public static class MyContextResolver implements ContextResolver<String> {
+        private int i = 1;
+
+        @Override
+        public String getContext(Class<?> type) {
+            if (type == String.class) {
+                return "context:" + i++;
+            }
+            return null;
+        }
+    }
+
+    @Produces({"text/test"})
+    @Consumes({"text/test"})
+    public static final class SingletonStringMessageProvider extends AbstractMessageReaderWriterProvider<String> {
+        private int counter = 1;
+        private int readerCounter = 1;
+
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public String readFrom(
+                Class<String> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException {
+            return readFromAsString(entityStream, mediaType) + this + ":" + readerCounter++;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(
+                String t,
+                Class<?> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException {
+            writeToAsString(t + this + ":" + counter++, entityStream, mediaType);
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProvidersResourcesTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProvidersResourcesTest.java
new file mode 100644
index 0000000..19a6f8d
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProvidersResourcesTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2012, 2018 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.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.internal.inject.PerLookup;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test resources which also acts as providers.
+ *
+ * @author Miroslav Fuksa
+ */
+public class SingletonProvidersResourcesTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(ResourceSingleton.class, ResourceNotSingleton.class);
+
+        final Resource.Builder resourceBuilder = Resource.builder();
+        resourceBuilder.name("programmatic").path("programmatic").addMethod("GET")
+                .handledBy(ResourceProgrammaticNotSingleton.class);
+        resourceConfig.registerResources(resourceBuilder.build());
+
+        return resourceConfig;
+    }
+
+    @Test
+    public void testResourceAsFilter() {
+        String str = target().path("singleton").request().header("singleton", "singleton").get(String.class);
+        assertTrue(str, str.startsWith("true/"));
+        String str2 = target().path("singleton").request().header("singleton", "singleton").get(String.class);
+        assertTrue(str2, str2.startsWith("true/"));
+        assertEquals(str, str2);
+    }
+
+    @Test
+    public void testResourceAsFilterAnnotatedPerLookup() {
+        String str = target().path("perlookup").request().header("not-singleton", "not-singleton").get(String.class);
+        assertTrue(str.startsWith("false/"));
+        String str2 = target().path("perlookup").request().header("not-singleton", "not-singleton").get(String.class);
+        assertTrue(str2.startsWith("false/"));
+        assertNotSame(str, str2);
+    }
+
+    @Test
+    public void testResourceProgrammatic() {
+        String str = target().path("programmatic").request().header("programmatic", "programmatic").get(String.class);
+        assertTrue(str.startsWith("false/"));
+        String str2 = target().path("programmatic").request().header("programmatic", "programmatic").get(String.class);
+        assertTrue(str2.startsWith("false/"));
+        assertNotSame(str, str2);
+    }
+
+    // this should be singleton, it means the same instance for the usage as a filter and as an resource
+    @Path("singleton")
+    public static class ResourceSingleton implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if (requestContext.getHeaders().containsKey("singleton")) {
+                requestContext.getHeaders().add("filter-class", this.toString());
+            }
+        }
+
+        @GET
+        public String get(@HeaderParam("filter-class") String filterClass) {
+            return String.valueOf(String.valueOf(this.toString().equals(filterClass)) + "/" + this.toString() + ":"
+                    + filterClass);
+        }
+    }
+
+    // this should NOT be singleton, because it is annotated as per lookup
+    @Path("perlookup")
+    @PerLookup
+    public static class ResourceNotSingleton implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if (requestContext.getHeaders().containsKey("not-singleton")) {
+                requestContext.getHeaders().add("filter-class", this.toString());
+            }
+        }
+
+        @GET
+        public String get(@HeaderParam("filter-class") String filterClass) {
+            return String.valueOf(String.valueOf(this.toString().equals(filterClass)) + "/" + this.toString() + ":"
+                    + filterClass);
+        }
+    }
+
+    // should not be a singleton as this is only programmatic resource and is not registered as provider
+    public static class ResourceProgrammaticNotSingleton implements ContainerRequestFilter,
+                                                                    Inflector<Request, Response> {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            if (requestContext.getHeaders().containsKey("programmatic")) {
+                requestContext.getHeaders().add("filter-class", this.toString());
+            }
+        }
+
+        @Override                            //JerseyContainerRequestContext
+        public Response apply(Request request) {
+            String filterClass = ((ContainerRequestContext) request).getHeaders().getFirst("filter-class");
+            return Response.ok(String.valueOf(String.valueOf(this.toString().equals(filterClass)) + "/" + this.toString() + ":"
+                    + filterClass)).build();
+        }
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonResourceTest.java
new file mode 100644
index 0000000..1073e09
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonResourceTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2012, 2018 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 javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.internal.inject.PerLookup;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Class testing Resources managed as singletons.
+ *
+ * @author Miroslav Fuksa
+ */
+public class SingletonResourceTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(
+                SingletonResource.class,
+                ChildInheritsParentAnnotation.class,
+                ChildImplementsInterfaceAnnotation.class,
+                TestResource.class,
+                RequestScopeResource.class,
+                PerLookupScopeResource.class,
+                SingletonScopeResource.class);
+
+        final Resource.Builder resourceBuilder1 = Resource.builder();
+        resourceBuilder1.name("resource-programmatic/instance/").path("programmatic/instance/").addMethod("GET")
+                .handledBy(new Inflector<ContainerRequestContext, Response>() {
+                    private int counter = 1;
+
+                    @Override
+                    public Response apply(ContainerRequestContext data) {
+                        return Response.ok("prg-instance:" + counter++).build();
+                    }
+                });
+        resourceConfig.registerResources(resourceBuilder1.build());
+
+        final Resource.Builder resourceBuilder2 = Resource.builder();
+        resourceBuilder2.name("resource-programmatic/singleton/").path("programmatic/singleton/").addMethod("GET")
+                .handledBy(SingletonProgrammatic.class);
+        resourceConfig.registerResources(resourceBuilder2.build());
+
+        final Resource.Builder resourceBuilder3 = Resource.builder();
+        resourceBuilder3.name("resource-programmatic/reused-singleton/").path("programmatic/reused-singleton/").addMethod("GET")
+                .handledBy(SubResourceSingleton.class);
+        resourceConfig.registerResources(resourceBuilder3.build());
+
+        final Resource.Builder resourceBuilder4 = Resource.builder();
+        resourceBuilder4.name("resource-programmatic/not-singleton/").path("programmatic/not-singleton/").addMethod("GET")
+                .handledBy(NotSingletonProgrammatic.class);
+        resourceConfig.registerResources(resourceBuilder4.build());
+
+        return resourceConfig;
+    }
+
+    @Test
+    public void singletonResourceTest() {
+        String str;
+        str = target().path("singleton").request().get().readEntity(String.class);
+        assertEquals("res:1", str);
+
+        str = target().path("singleton").request().get().readEntity(String.class);
+        assertEquals("res:2", str);
+
+        str = target().path("singleton/sub").request().get().readEntity(String.class);
+        assertEquals("sub:1", str);
+
+        str = target().path("singleton").request().get().readEntity(String.class);
+        assertEquals("res:3", str);
+
+        str = target().path("singleton/sub").request().get().readEntity(String.class);
+        assertEquals("sub:2", str);
+
+        str = target().path("singleton/sub-not-singleton").request().get().readEntity(String.class);
+        assertEquals("not-singleton:1", str);
+
+        str = target().path("singleton/sub-not-singleton").request().get().readEntity(String.class);
+        assertEquals("not-singleton:1", str);
+
+        str = target().path("singleton/instance").request().get().readEntity(String.class);
+        assertEquals("sub:1", str);
+
+        str = target().path("singleton/instance").request().get().readEntity(String.class);
+        assertEquals("sub:1", str);
+
+        str = target().path("singleton/sub").request().get().readEntity(String.class);
+        assertEquals("sub:3", str);
+
+        // one instance
+        str = target().path("programmatic").path("instance").request().get().readEntity(String.class);
+        assertEquals("prg-instance:1", str);
+
+        str = target().path("programmatic").path("instance").request().get().readEntity(String.class);
+        assertEquals("prg-instance:2", str);
+
+        // singleton
+        str = target().path("programmatic").path("singleton").request().get().readEntity(String.class);
+        assertEquals("prg-singleton:1", str);
+
+        str = target().path("programmatic").path("singleton").request().get().readEntity(String.class);
+        assertEquals("prg-singleton:2", str);
+
+        // request to the SubResourceSingleton (same class as sub resource on path "singleton/sub")
+        str = target().path("programmatic").path("reused-singleton").request().get().readEntity(String.class);
+        assertEquals("reused-singleton:4", str);
+
+        // not singleton
+        str = target().path("programmatic").path("not-singleton").request().get().readEntity(String.class);
+        assertEquals("prg-not-singleton:1", str);
+
+        str = target().path("programmatic").path("not-singleton").request().get().readEntity(String.class);
+        assertEquals("prg-not-singleton:1", str);
+    }
+
+    @Test
+    public void singletonAnnotationInheritedTest() {
+        // Singleton annotation is not inherited
+        String str;
+        str = target().path("inherit").request().get().readEntity(String.class);
+        assertEquals("inherit:1", str);
+
+        str = target().path("inherit").request().get().readEntity(String.class);
+        assertEquals("inherit:1", str);
+    }
+
+    @Test
+    public void singletonAnnotationInterfaceTest() {
+        // Singleton annotation is not inherited
+        String str;
+        str = target().path("interface").request().get().readEntity(String.class);
+        assertEquals("interface:1", str);
+
+        str = target().path("interface").request().get().readEntity(String.class);
+        assertEquals("interface:1", str);
+
+    }
+
+    /**
+     * Tests that resources are by default managed in {@link org.glassfish.jersey.process.internal.RequestScope request scope}.
+     */
+    @Test
+    public void testResourceInRequestScope() {
+        String str = target().path("testScope/request").request().get().readEntity(String.class);
+        assertEquals("same-instances", str);
+    }
+
+    @Test
+    public void testResourceInPerLookupScope() {
+        String str = target().path("testScope/perlookup").request().get().readEntity(String.class);
+        assertEquals("different-instances", str);
+    }
+
+    @Test
+    public void testResourceInSingletonScope() {
+        String str = target().path("testScope/singleton").request().get().readEntity(String.class);
+        assertEquals("same-instances", str);
+    }
+
+    @Path("test-requestScope")
+    public static class RequestScopeResource {
+
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Path("test-perlookupScope")
+    @PerLookup
+    public static class PerLookupScopeResource {
+
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Path("test-singletonScope")
+    @Singleton
+    public static class SingletonScopeResource {
+
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Path("testScope")
+    public static class TestResource {
+
+        @Inject
+        InjectionManager injectionManager;
+
+        private String compareInstances(Class<?> clazz) {
+            final Object res1 = Injections.getOrCreate(injectionManager, clazz);
+            final Object res2 = Injections.getOrCreate(injectionManager, clazz);
+            return (res1 == res2) ? "same-instances" : "different-instances";
+        }
+
+        @GET
+        @Path("request")
+        public String compareRequestScopedInstances() {
+            return compareInstances(RequestScopeResource.class);
+        }
+
+        @GET
+        @Path("perlookup")
+        public String comparePerLookupScopedInstances() {
+            return compareInstances(PerLookupScopeResource.class);
+        }
+
+        @GET
+        @Path("singleton")
+        public String compareSingletonInstances() {
+            return compareInstances(SingletonScopeResource.class);
+        }
+    }
+
+    @Singleton
+    public static class Parent {
+
+    }
+
+    @Path("inherit")
+    public static class ChildInheritsParentAnnotation extends Parent {
+
+        private int counter = 1;
+
+        @GET
+        public String get() {
+            return "inherit:" + counter++;
+        }
+    }
+
+    @Singleton
+    public static interface AnnotatedBySingleton {
+    }
+
+    @Path("interface")
+    public static class ChildImplementsInterfaceAnnotation implements AnnotatedBySingleton {
+
+        private int counter = 1;
+
+        @GET
+        public String get() {
+            return "interface:" + counter++;
+        }
+    }
+
+    @Singleton
+    public static class SingletonProgrammatic implements Inflector<Request, Response> {
+
+        private int counter = 1;
+
+        @Override
+        public Response apply(Request data) {
+            return Response.ok("prg-singleton:" + counter++).build();
+        }
+
+    }
+
+    public static class NotSingletonProgrammatic implements Inflector<Request, Response> {
+
+        private int counter = 1;
+
+        @Override
+        public Response apply(Request data) {
+            return Response.ok("prg-not-singleton:" + counter++).build();
+        }
+
+    }
+
+    @Singleton
+    @Path("singleton")
+    public static class SingletonResource {
+
+        private int counter = 1;
+
+        @GET
+        @Produces("text/html")
+        public String getCounter() {
+            return "res:" + (counter++);
+        }
+
+        @Path("sub")
+        public Class getSubResourceSingleton() {
+            return SubResourceSingleton.class;
+        }
+
+        @Path("sub-not-singleton")
+        public Class getSubResource() {
+            return SubResource.class;
+        }
+
+        @Path("instance")
+        public Object getSubResourceInstance() {
+            return new SubResourceSingleton();
+        }
+
+        @GET
+        @Path("filter")
+        public String getCounterFromFilter(@HeaderParam("counter") int counter) {
+            return "filter:" + counter;
+        }
+
+    }
+
+    @Singleton
+    public static class SubResourceSingleton implements Inflector<Request, Response> {
+
+        private int counter = 1;
+
+        @GET
+        public String getInternalCounter() {
+            return "sub:" + (counter++);
+        }
+
+        @Override
+        public Response apply(Request request) {
+            return Response.ok("reused-singleton:" + counter++).build();
+        }
+    }
+
+    public static class SubResource {
+
+        private int counter = 1;
+
+        @GET
+        public String getInternalCounter() {
+            return "not-singleton:" + (counter++);
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/StreamMethodCallTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/StreamMethodCallTest.java
new file mode 100644
index 0000000..7443489
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/StreamMethodCallTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013, 2018 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.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests that appropriate methods are called in the intercepted output stream.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class StreamMethodCallTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestWriterInterceptor.class, TestResource.class);
+    }
+
+    public static class TestWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final OutputStream outputStreamoldOs = context.getOutputStream();
+            context.setOutputStream(new TestOutputStream(new GZIPOutputStream(outputStreamoldOs)));
+            context.proceed();
+        }
+    }
+
+    public static class TestOutputStream extends OutputStream {
+        private final GZIPOutputStream gzos;
+        public static boolean closeCalled = false;
+        public static boolean flushCalledBeforeClose = false;
+        public static boolean writeCalled = false;
+
+        public TestOutputStream(GZIPOutputStream gzos) {
+            this.gzos = gzos;
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            writeCalled = true;
+            gzos.write(b);
+        }
+
+        @Override
+        public void close() throws IOException {
+            TestOutputStream.closeCalled = true;
+            gzos.close();
+        }
+
+        @Override
+        public void flush() throws IOException {
+            if (!closeCalled) {
+                flushCalledBeforeClose = true;
+            }
+            gzos.flush();
+        }
+    }
+
+    @Path("resource")
+    public static class TestResource {
+        @GET
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Test
+    public void testCalledMethods() {
+        final Response response = target().path("resource").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertTrue("close() has not been called.", TestOutputStream.closeCalled);
+        Assert.assertTrue("flush() has not been called before close().", TestOutputStream.flushCalledBeforeClose);
+        Assert.assertTrue("write() has not been called.", TestOutputStream.writeCalled);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SubResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SubResourceTest.java
new file mode 100644
index 0000000..5ce4830
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SubResourceTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2012, 2018 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.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Sub-resource access/processing E2E tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Miroslav Fuksa
+ */
+public class SubResourceTest extends JerseyTest {
+
+    @Path("root/sub")
+    public static class Resource {
+        @Path("/")
+        public SubResource getSubResourceLocator() {
+            return new SubResource();
+        }
+
+        @Path("sub2")
+        public SubResource getSubResourceLocator2() {
+            return new SubResource();
+        }
+
+        static final String GET = "get";
+
+        @Path("some/path")
+        @GET
+        public String get() {
+            return GET;
+        }
+
+        @Path("empty-locator")
+        public EmptySubResourceClass getEmptyLocator() {
+            return new EmptySubResourceClass();
+        }
+    }
+
+    public static class SubResource {
+        public static final String MESSAGE = "Got it!";
+
+        @GET
+        public String getIt() {
+            return MESSAGE;
+        }
+
+
+        @POST
+        public String post(String str) {
+            return str;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, LocatorAndMethodResource.class, EmptyRootResource.class);
+    }
+
+    /**
+     * Test concurrent sub-resource access. (See JERSEY-1421).
+     *
+     * @throws Exception in case of test failure.
+     */
+    @Test
+    public void testConcurrentSubResourceAccess() throws Exception {
+        final WebTarget subResource = target("root/sub/sub2");
+
+        final int MAX = 25;
+
+        final List<Future<String>> results = new ArrayList<Future<String>>(MAX);
+        for (int i = 0; i < MAX; i++) {
+            results.add(subResource.request().async().get(String.class));
+        }
+
+        for (Future<String> resultFuture : results) {
+            assertEquals(SubResource.MESSAGE, resultFuture.get());
+        }
+    }
+
+    @Test
+    public void subResourceTest() throws Exception {
+        Response response = target("root/sub/sub2").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(SubResource.MESSAGE, response.readEntity(String.class));
+
+        response = target("root/sub/sub2").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(SubResource.MESSAGE, response.readEntity(String.class));
+    }
+
+    @Test
+    public void subResourceWithoutPathTest() throws Exception {
+        Response response = target("root/sub").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(SubResource.MESSAGE, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        Response response = target("root/sub/some/path").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals(Resource.GET, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPost() throws Exception {
+        Response response = target("root/sub/sub2").request().post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("post", response.readEntity(String.class));
+    }
+
+    // this resource class will report warning during validation, but should be loaded
+    @Path("locator-and-method")
+    public static class LocatorAndMethodResource {
+        @GET
+        @Path("sub")
+        public String getSub() {
+            return "get";
+        }
+
+        @Path("sub")
+        public PostSubResource getSubResourceSub() {
+            return new PostSubResource();
+        }
+
+        @GET
+        public String get() {
+            return "get";
+        }
+
+        @Path("/")
+        public PostSubResource getSubResource() {
+            return new PostSubResource();
+        }
+    }
+
+    public static class PostSubResource {
+        @GET
+        public String get() {
+            return "fail: locator get should never be called !!!";
+        }
+
+        @POST
+        public String post(String post) {
+            return "fail: post should never be called !!!";
+        }
+
+        @GET
+        @Path("inner")
+        public String getInner() {
+            return "inner";
+        }
+    }
+
+    @Test
+    public void testGetIsCalled() throws Exception {
+        Response response = target("locator-and-method").request().get();
+        Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        Assert.assertEquals("get", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGetIsCalledInSub() throws Exception {
+        Response response = target("locator-and-method/sub").request().get();
+        Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        Assert.assertEquals("get", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGetIsCalledInInner() throws Exception {
+        Response response = target("locator-and-method/inner").request().get();
+        Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        Assert.assertEquals("inner", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGetIsCalledInSubInner() throws Exception {
+        Response response = target("locator-and-method/sub/inner").request().get();
+        Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        Assert.assertEquals("inner", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPostShouldNeverBeCalled() throws Exception {
+        Response response = target("locator-and-method").request().post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(Response.Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
+    }
+
+    @Test
+    public void testPostShouldNeverBeCalledInSub() throws Exception {
+        Response response = target("locator-and-method/sub").request().post(Entity.entity("post", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(Response.Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
+    }
+
+    @Path("empty-root")
+    public static class EmptyRootResource {
+
+    }
+
+    @Test
+    public void testCallEmptyResource() throws Exception {
+        Response response = target("empty-root").request().get();
+        Assert.assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
+    }
+
+    public static class EmptySubResourceClass {
+        // empty
+    }
+
+    @Test
+    public void testCallEmptySubResource() throws Exception {
+        Response response = target("empty-locator").request().get();
+        Assert.assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SubjectSecurityContextTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SubjectSecurityContextTest.java
new file mode 100644
index 0000000..6d2d583
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SubjectSecurityContextTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2011, 2018 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.io.IOException;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotAcceptableException;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.SubjectSecurityContext;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test subject based security context. Make sure resource
+ * and sub-resource methods/locators are invoked
+ * via {@link SubjectSecurityContext#doAsSubject(java.security.PrivilegedAction)} method.
+ *
+ * @author Martin Matula
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class SubjectSecurityContextTest extends JerseyTest {
+
+    // actual filter reference to keep track of the invocations
+    SubjectSecurityContextSettingFilter subjectFilter;
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class).registerInstances(subjectFilter = new SubjectSecurityContextSettingFilter());
+    }
+
+    @Path("/resource")
+    public static class Resource {
+
+        @GET
+        public String resourceGet() {
+            return "Resource GET";
+        }
+
+        @Path("subresource")
+        public SubResource getSubResource() {
+            return new SubResource();
+        }
+
+        @Path("subresource-wae")
+        public SubResource getSubResourceEx() {
+            throw new NotAcceptableException(Response.notAcceptable(null).entity("Not Acceptable SRL").build());
+        }
+
+        @Path("sub-get")
+        @GET
+        public String getSub() {
+            return "Resource sub-GET";
+        }
+
+        @Path("sub-get-wae")
+        @GET
+        public String getSubEx() {
+            throw new NotAcceptableException(Response.notAcceptable(null).entity("Not Acceptable Resource sub-GET").build());
+        }
+    }
+
+    public static class SubResource {
+        @GET
+        public String subResourceGet() {
+            return "SubResource GET";
+        }
+
+        @Path("wae")
+        @GET
+        public String subResourceGetEx() {
+            throw new NotAcceptableException(Response.notAcceptable(null).entity("Not Acceptable SubResource GET").build());
+        }
+    }
+
+    /**
+     * Custom SubjectSecurityContext that keeps number of doAsSubject invocations.
+     */
+    public static class MySubjectSecurityContext implements SubjectSecurityContext {
+
+        // no hits so far
+        int hits = 0;
+
+        @Override
+        public Object doAsSubject(PrivilegedAction action) {
+            hits++;
+            return action.run();
+        }
+
+        @Override
+        public Principal getUserPrincipal() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public boolean isUserInRole(String string) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public boolean isSecure() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public String getAuthenticationScheme() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    /**
+     * Custom filter to set the custom subject security context on every request before the request get matched,
+     * so that sub-resource locator invocations have a chance to see the subject security context set.
+     */
+    @PreMatching
+    @Provider
+    public static class SubjectSecurityContextSettingFilter implements ContainerRequestFilter {
+
+        private final MySubjectSecurityContext securityContext = new MySubjectSecurityContext();
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            requestContext.setSecurityContext(securityContext);
+        }
+
+        /**
+         * Provide actual doAsSubject hit count.
+         *
+         * @return number of doAsSubject invocations.
+         */
+        public int getHits() {
+            return securityContext.hits;
+        }
+    }
+
+    @Test
+    public void testSubjectSecurityContext() {
+
+        WebTarget r = target("/resource");
+
+        assertThat(r.request().get(String.class), equalTo("Resource GET"));
+        assertThat(subjectFilter.getHits(), equalTo(1)); // one resource method invoked
+
+        assertThat(r.path("subresource").request().get(String.class), equalTo("SubResource GET"));
+        assertThat(subjectFilter.getHits(), equalTo(3)); // + one sub-resource locator and one resource method invoked
+
+        assertThat("Resource sub-GET", equalTo(r.path("sub-get").request().get(String.class)));
+        assertThat(subjectFilter.getHits(), equalTo(4)); // + one sub-resource method invoked
+
+        Response response;
+
+        response = r.path("sub-get-wae").request().get();
+        assertThat(response.getStatus(), equalTo(Response.Status.NOT_ACCEPTABLE.getStatusCode()));
+        assertThat(response.readEntity(String.class), equalTo("Not Acceptable Resource sub-GET"));
+        assertThat(subjectFilter.getHits(), equalTo(5)); // + one sub-resource method invoked
+
+        response = r.path("subresource-wae").request().get();
+        assertThat(response.getStatus(), equalTo(Response.Status.NOT_ACCEPTABLE.getStatusCode()));
+        assertThat(response.readEntity(String.class), equalTo("Not Acceptable SRL"));
+        assertThat(subjectFilter.getHits(), equalTo(6));
+
+        response = r.path("subresource/wae").request().get();
+        assertThat(response.getStatus(), equalTo(Response.Status.NOT_ACCEPTABLE.getStatusCode()));
+        assertThat(response.readEntity(String.class), equalTo("Not Acceptable SubResource GET"));
+        assertThat(subjectFilter.getHits(), equalTo(8)); // + one sub-resource locator and one resource method invoked
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/UriBuilderTemplateTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/UriBuilderTemplateTest.java
new file mode 100644
index 0000000..f30c82c
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/UriBuilderTemplateTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012, 2018 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.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Testing URI template as an e2e test.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class UriBuilderTemplateTest extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testNumericResource() {
+        Response response = target().path("test/numeric/55").request().get();
+        String uri = response.readEntity(String.class);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals("test/numeric/55", uri);
+    }
+
+    @Test
+    public void testAnyResource() {
+        Response response = target().path("test/any/getNumericResource").request().get();
+        String uri = response.readEntity(String.class);
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals("test/numeric/11", uri);
+    }
+
+    @Path("test")
+    public static class Resource {
+
+        @Path("numeric/{ resourceNo : \\d+}")
+        @GET
+        public String getNumericResource(@PathParam("resourceNo") int resourceNo) {
+            URI anyResourceUri = UriBuilder.fromResource(Resource.class).path(Resource.class,
+                    "getNumericResource").build(resourceNo);
+            return anyResourceUri.toString();
+        }
+
+        @Path("any/{resourceName}")
+        @GET
+        public String getAnyResource(@PathParam("resourceName") String resourceName) {
+            URI numResourceUri = UriBuilder.fromResource(Resource.class).path(Resource.class, "getNumericResource").build(11);
+            return numResourceUri.toString();
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testResolveTemplate() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("a", "xyz");
+        map.put(null, "path");
+        UriBuilder builder = UriBuilder.fromPath("").path("{a}/{b}");
+        builder.resolveTemplates(map);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/WebApplicationExceptionLoggingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/WebApplicationExceptionLoggingTest.java
new file mode 100644
index 0000000..7f07197
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/WebApplicationExceptionLoggingTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013, 2018 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.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import javax.validation.ValidationException;
+import javax.validation.constraints.NotNull;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests that {@link WebApplicationException} is logged on the correct level.
+ *
+ * @author Miroslav Fuksa
+ */
+public class WebApplicationExceptionLoggingTest extends JerseyTest {
+
+    @Path("/test")
+    public static class StatusResource {
+
+        @GET
+        @Produces("text/plain")
+        public String test(@NotNull @QueryParam("id") final String id) {
+            return "ok";
+        }
+
+        @GET
+        @Path("WAE-no-entity")
+        @Produces("text/plain")
+        public String testWithoutEntity() {
+            throw new WebApplicationException("WAE without entity", Response.status(400).build());
+        }
+
+        @GET
+        @Path("WAE-entity")
+        @Produces("text/plain")
+        public String testWithEntity() {
+            throw new WebApplicationException("WAE with entity", Response.status(400).entity("WAE with entity").build());
+        }
+    }
+
+    @Provider
+    public static class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
+
+        @Override
+        public Response toResponse(final ValidationException ex) {
+            return Response.status(200).entity("Error mapped: " + ex.toString()).type("text/plain").build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        set(TestProperties.RECORD_LOG_LEVEL, Level.FINER.intValue());
+
+        return new ResourceConfig(StatusResource.class, ValidationExceptionMapper.class);
+    }
+
+    private LogRecord getLogRecord(final String messagePrefix) {
+        for (final LogRecord logRecord : getLoggedRecords()) {
+            if (logRecord.getMessage() != null && logRecord.getMessage().startsWith(messagePrefix)) {
+                return logRecord;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testValidationException() {
+        final Response response = target().path("test").request().get();
+        assertEquals(200, response.getStatus());
+
+        final String entity = response.readEntity(String.class);
+        assertTrue(entity.startsWith("Error mapped:"));
+
+        // check logs
+        final LogRecord logRecord = this.getLogRecord("Starting mapping of the exception");
+        assertNotNull(logRecord);
+        assertEquals(Level.FINER, logRecord.getLevel());
+
+        // check that there is no exception logged on the level higher than FINE
+        for (final LogRecord record : getLoggedRecords()) {
+            if (record.getThrown() != null) {
+                assertTrue(record.getLevel().intValue() <= Level.FINE.intValue());
+            }
+        }
+
+    }
+
+    @Test
+    public void testWAEWithEntity() {
+        final Response response = target().path("test/WAE-entity").request().get();
+        assertEquals(400, response.getStatus());
+        final String entity = response.readEntity(String.class);
+        assertEquals("WAE with entity", entity);
+
+        // check logs
+        LogRecord logRecord = this.getLogRecord("Starting mapping of the exception");
+        assertNotNull(logRecord);
+        assertEquals(Level.FINER, logRecord.getLevel());
+
+        logRecord = this.getLogRecord("WebApplicationException (WAE) with non-null entity thrown.");
+        assertNotNull(logRecord);
+        assertEquals(Level.FINE, logRecord.getLevel());
+        assertTrue(logRecord.getThrown() instanceof WebApplicationException);
+        logRecord.getThrown().printStackTrace();
+    }
+
+    @Test
+    public void testWAEWithoutEntity() {
+        final Response response = target().path("test/WAE-no-entity").request().get();
+        assertEquals(400, response.getStatus());
+        assertFalse(response.hasEntity());
+
+        // check logs
+        LogRecord logRecord = this.getLogRecord("Starting mapping of the exception");
+        assertNotNull(logRecord);
+        assertEquals(Level.FINER, logRecord.getLevel());
+
+        logRecord = this.getLogRecord("WebApplicationException (WAE) with no entity thrown and no");
+        assertNotNull(logRecord);
+        assertEquals(Level.FINE, logRecord.getLevel());
+        assertTrue(logRecord.getThrown() instanceof WebApplicationException);
+        logRecord.getThrown().printStackTrace();
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/PostToPutDeleteTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/PostToPutDeleteTest.java
new file mode 100644
index 0000000..414159a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/PostToPutDeleteTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 2018 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.filter;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.filter.HttpMethodOverrideFilter;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Martin Matula
+ */
+public class PostToPutDeleteTest extends JerseyTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String get(@QueryParam("a") String a) {
+            return "GET: " + a;
+        }
+
+        @PUT
+        public String put() {
+            return "PUT";
+        }
+
+        @DELETE
+        public String delete() {
+            return "DELETE";
+        }
+
+        @POST
+        public String post() {
+            return "POST";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HttpMethodOverrideFilter.class, Resource.class);
+    }
+
+    @Test
+    public void testPut() {
+        assertResponseEquals("PUT,PUT,PUT,", _test("PUT"));
+    }
+
+    @Test
+    public void testDelete() {
+        assertResponseEquals("DELETE,DELETE,DELETE,", _test("DELETE"));
+    }
+
+    @Test
+    public void testGet() {
+        assertResponseEquals("GET: test,GET: test,GET: test,", _test("GET"));
+    }
+
+    @Test
+    public void testConflictingMethods() {
+        Response cr = target("/").queryParam("_method", "PUT").request()
+                .header("X-HTTP-Method-Override", "DELETE").post(Entity.text(""));
+        assertEquals(400, cr.getStatus());
+    }
+
+    @Test
+    public void testUnsupportedMethod() {
+        assertResponseEquals("405,405,405,", _test("PATCH"));
+    }
+
+    @Test
+    public void testGetWithQueryParam() {
+        String result = target().queryParam("_method", "GET").queryParam("a", "test").request().post(null, String.class);
+        assertEquals("GET: test", result);
+    }
+
+    @Test
+    public void testGetWithOtherEntity() {
+        String result = target().queryParam("_method", "GET").request().post(Entity.text("a=test"), String.class);
+        assertEquals("GET: null", result);
+    }
+
+    @Test
+    public void testPlainPost() {
+        String result = target().request().post(null, String.class);
+        assertEquals("POST", result);
+    }
+
+    public Response[] _test(String method) {
+        Response[] result = new Response[3];
+        WebTarget target = target();
+
+        result[0] = target.request().header("X-HTTP-Method-Override", method)
+                .post(Entity.form(new Form().param("a", "test")));
+        result[1] = target.queryParam("_method", method).request()
+                .post(Entity.form(new Form().param("a", "test")));
+        result[2] = target.queryParam("_method", method).request().header("X-HTTP-Method-Override", method)
+                .post(Entity.form(new Form().param("a", "test")));
+        return result;
+    }
+
+    public void assertResponseEquals(String expected, Response[] responses) {
+        StringBuilder result = new StringBuilder();
+
+        for (Response r : responses) {
+            if (r.getStatus() == Response.Status.OK.getStatusCode()) {
+                result.append(r.readEntity(String.class));
+            } else {
+                result.append(r.getStatus());
+            }
+            result.append(",");
+        }
+
+        assertEquals(expected, result.toString());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/RolesAllowedTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/RolesAllowedTest.java
new file mode 100644
index 0000000..4dda6f3
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/RolesAllowedTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2010, 2018 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.filter;
+
+import java.security.Principal;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import javax.annotation.Priority;
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ *
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class RolesAllowedTest extends JerseyTest {
+
+    @PreMatching
+    @Priority(Priorities.AUTHENTICATION)
+    public static class SecurityFilter implements ContainerRequestFilter {
+
+        public void filter(final ContainerRequestContext request) {
+            final String user = request.getHeaders().getFirst("X-USER");
+            request.setSecurityContext(new Authenticator(user));
+        }
+
+        private static class Authenticator implements SecurityContext {
+
+            private final Principal principal;
+
+            Authenticator(final String name) {
+                principal = name == null
+                        ? null
+                        : new Principal() {
+                            public String getName() {
+                                return name;
+                            }
+                        };
+            }
+
+            public Principal getUserPrincipal() {
+                return principal;
+            }
+
+            public boolean isUserInRole(final String role) {
+                return role.equals(principal.getName()) || ("user".equals(role) && "admin".equals(principal.getName()));
+            }
+
+            public boolean isSecure() {
+                return false;
+            }
+
+            public String getAuthenticationScheme() {
+                return "";
+            }
+        }
+    }
+
+    @Path("/")
+    @PermitAll
+    public static class Resource {
+
+        @RolesAllowed("user")
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @RolesAllowed("admin")
+        @POST
+        public String post(final String content) {
+            return content;
+        }
+
+        @Path("sub")
+        public SubResource getSubResource() {
+            return new SubResource();
+        }
+    }
+
+    @RolesAllowed("admin")
+    public static class SubResource {
+
+        @Path("deny-all")
+        @DenyAll
+        @GET
+        public String denyAll() {
+            return "GET";
+        }
+
+        @Path("permit-all")
+        @PermitAll
+        @GET
+        public String permitAll() {
+            return "GET";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, RolesAllowedDynamicFeature.class, SecurityFilter.class);
+    }
+
+    @Test
+    public void testGetAsUser() {
+        assertEquals("GET", target().request().header("X-USER", "user").get(String.class));
+    }
+
+    @Test
+    public void testGetAsAdmin() {
+        assertEquals("GET", target().request().header("X-USER", "admin").get(String.class));
+    }
+
+    @Test
+    public void testPostAsUser() {
+        final Response cr = target().request().header("X-USER", "user").post(Entity.text("POST"));
+        assertEquals(403, cr.getStatus());
+    }
+
+    @Test
+    public void testPostAsAdmin() {
+        assertEquals("POST", target().request().header("X-USER", "admin").post(Entity.text("POST"), String.class));
+    }
+
+    @Test
+    public void testDenyAll() {
+        assertEquals(403, target("sub/deny-all").request().header("X-USER", "admin").get().getStatus());
+    }
+
+    @Test
+    public void testPermitAll() {
+        assertEquals("GET", target("sub/permit-all").request().header("X-USER", "xyz").get(String.class));
+    }
+
+    @Test
+    public void testNotAuthorized() {
+        assertThat("User should not be authorized.", target().request().get().getStatus(), is(403));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeNegativeTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeNegativeTest.java
new file mode 100644
index 0000000..4c191dd
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeNegativeTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 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.filter;
+
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * No activation property ({@value org.glassfish.jersey.server.ServerProperties#LANGUAGE_MAPPINGS},
+ * {@value org.glassfish.jersey.server.ServerProperties#MEDIA_TYPE_MAPPINGS}) is set into {@link ResourceConfig} which should
+ * lead to point that {@link org.glassfish.jersey.server.filter.UriConnegFilter} is not registered.
+ *
+ * @author Michal Gajdos
+ */
+public class UriConnegLanguageMediaTypeNegativeTest extends JerseyTest {
+
+    @Path("/abc")
+    public static class LanguageVariantResource {
+
+        @GET
+        public Response doGet(@Context Request r) {
+            final List<Variant> variants = Variant.VariantListBuilder.newInstance()
+                    .mediaTypes(MediaType.valueOf("application/foo"))
+                    .languages(new Locale("en")).languages(new Locale("fr")).add()
+                    .mediaTypes(MediaType.valueOf("application/bar"))
+                    .languages(new Locale("en")).languages(new Locale("fr")).add()
+                    .build();
+
+            final Variant variant = r.selectVariant(variants);
+            if (variant == null) {
+                return Response.notAcceptable(variants).build();
+            } else {
+                return Response.ok(variant.getMediaType().toString() + ", " + variant.getLanguage(), variant).build();
+            }
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(LanguageVariantResource.class).register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testMediaTypesAndLanguagesNegative() {
+        _test("english", "foo", "en", "application/foo");
+        _test("french", "foo", "fr", "application/foo");
+
+        _test("english", "bar", "en", "application/bar");
+        _test("french", "bar", "fr", "application/bar");
+    }
+
+    private void _test(String ul, String um, String l, String m) {
+        Response response = target().path("abc." + ul + "." + um).request().get();
+        assertEquals(404, response.getStatus());
+
+        response = target().path("abc." + um + "." + ul).request().get();
+        assertEquals(404, response.getStatus());
+
+        response = target().path("abc").request(m).header(HttpHeaders.ACCEPT_LANGUAGE, l).get();
+        assertEquals(m + ", " + l, response.readEntity(String.class));
+        assertEquals(l, response.getLanguage().toString());
+        assertEquals(m, response.getMediaType().toString());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeTest.java
new file mode 100644
index 0000000..635b499
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010, 2018 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.filter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class UriConnegLanguageMediaTypeTest extends JerseyTest {
+
+    @Path("/abc")
+    public static class LanguageVariantResource {
+
+        @GET
+        public Response doGet(@Context Request r) {
+            final List<Variant> variants = Variant.VariantListBuilder.newInstance()
+                    .mediaTypes(MediaType.valueOf("application/foo"))
+                    .languages(new Locale("en")).languages(new Locale("fr")).add()
+                    .mediaTypes(MediaType.valueOf("application/bar"))
+                    .languages(new Locale("en")).languages(new Locale("fr")).add()
+                    .build();
+
+            final Variant variant = r.selectVariant(variants);
+            if (variant == null) {
+                return Response.notAcceptable(variants).build();
+            } else {
+                return Response.ok(variant.getMediaType().toString() + ", " + variant.getLanguage(), variant).build();
+            }
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        Map<String, MediaType> mediaTypes = new HashMap<>();
+        mediaTypes.put("foo", MediaType.valueOf("application/foo"));
+        mediaTypes.put("bar", MediaType.valueOf("application/bar"));
+
+        Map<String, String> languages = new HashMap<>();
+        languages.put("english", "en");
+        languages.put("french", "fr");
+
+        ResourceConfig rc = new ResourceConfig(LanguageVariantResource.class);
+        rc.property(ServerProperties.LANGUAGE_MAPPINGS, languages);
+        rc.property(ServerProperties.MEDIA_TYPE_MAPPINGS, mediaTypes);
+        return rc;
+    }
+
+    @Test
+    public void testMediaTypesAndLanguages() {
+        _test("english", "foo", "en", "application/foo");
+        _test("french", "foo", "fr", "application/foo");
+
+        _test("english", "bar", "en", "application/bar");
+        _test("french", "bar", "fr", "application/bar");
+    }
+
+    private void _test(String ul, String um, String l, String m) {
+        Response r = target().path("abc." + ul + "." + um).request().get();
+        assertEquals(m + ", " + l, r.readEntity(String.class));
+        assertEquals(l, r.getLanguage().toString());
+        assertEquals(m, r.getMediaType().toString());
+
+        r = target().path("abc." + um + "." + ul).request().get();
+        assertEquals(m + ", " + l, r.readEntity(String.class));
+        assertEquals(l, r.getLanguage().toString());
+        assertEquals(m, r.getMediaType().toString());
+
+        r = target().path("abc").request(m).header(HttpHeaders.ACCEPT_LANGUAGE, l).get();
+        assertEquals(m + ", " + l, r.readEntity(String.class));
+        assertEquals(l, r.getLanguage().toString());
+        assertEquals(m, r.getMediaType().toString());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageTest.java
new file mode 100644
index 0000000..25b537b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2010, 2018 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.filter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class UriConnegLanguageTest extends JerseyTest {
+
+    @Path("/abc")
+    public static class LanguageVariantResource {
+
+        @GET
+        public Response doGet(@Context Request request, @Context HttpHeaders headers) {
+
+            assertEquals(1, headers.getAcceptableLanguages().size());
+
+            List<Variant> vs = Variant.VariantListBuilder.newInstance()
+                    .languages(new Locale("zh"))
+                    .languages(new Locale("fr"))
+                    .languages(new Locale("en")).add()
+                    .build();
+
+            Variant v = request.selectVariant(vs);
+            if (v == null) {
+                return Response.notAcceptable(vs).build();
+            } else {
+                return Response.ok(v.getLanguage().toString(), v).build();
+            }
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        Map<String, String> languages = new HashMap<>();
+        languages.put("english", "en");
+        languages.put("french", "fr");
+
+        ResourceConfig rc = new ResourceConfig(LanguageVariantResource.class);
+        rc.property(ServerProperties.LANGUAGE_MAPPINGS, languages);
+        return rc;
+    }
+
+    @Test
+    public void testLanguages() {
+        Response response = target().path("abc.english").request().get();
+        assertEquals("en", response.readEntity(String.class));
+        assertEquals("en", response.getLanguage().toString());
+
+        response = target().path("abc.french").request().get();
+        assertEquals("fr", response.readEntity(String.class));
+        assertEquals("fr", response.getLanguage().toString());
+
+        response = target().path("abc.french").request().header(HttpHeaders.ACCEPT_LANGUAGE, "en").get();
+        assertEquals("fr", response.readEntity(String.class));
+        assertEquals("fr", response.getLanguage().toString());
+
+        response = target().path("abc").request().header(HttpHeaders.ACCEPT_LANGUAGE, "en").get();
+        assertEquals("en", response.readEntity(String.class));
+        assertEquals("en", response.getLanguage().toString());
+
+        response = target().path("abc").request().header(HttpHeaders.ACCEPT_LANGUAGE, "fr").get();
+        assertEquals("fr", response.readEntity(String.class));
+        assertEquals("fr", response.getLanguage().toString());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromProperty.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromProperty.java
new file mode 100644
index 0000000..c80ed48
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromProperty.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, 2018 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.filter;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * @author Martin Matula
+ */
+public class UriConnegMappingFromProperty extends UriConnegLanguageMediaTypeTest {
+
+    @Override
+    protected Application configure() {
+        ResourceConfig rc = new ResourceConfig(UriConnegLanguageMediaTypeTest.LanguageVariantResource.class);
+        rc.property(ServerProperties.MEDIA_TYPE_MAPPINGS, "foo : application/foo, bar : application/bar");
+        rc.property(ServerProperties.LANGUAGE_MAPPINGS, "english : en, french : fr");
+        return rc;
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromStringTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromStringTest.java
new file mode 100644
index 0000000..e7e0625
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromStringTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, 2018 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.filter;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * @author Martin Matula
+ */
+public class UriConnegMappingFromStringTest extends UriConnegLanguageMediaTypeTest {
+
+    @Override
+    protected Application configure() {
+        ResourceConfig rc = new ResourceConfig(LanguageVariantResource.class);
+        rc.property(ServerProperties.LANGUAGE_MAPPINGS,  "english : en, french : fr");
+        rc.property(ServerProperties.MEDIA_TYPE_MAPPINGS, "foo : application/foo, bar : application/bar");
+        return rc;
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMediaTypeTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMediaTypeTest.java
new file mode 100644
index 0000000..f842a32
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMediaTypeTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2010, 2018 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.filter;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Paul Sandoz
+ * @author Martin Matula
+ */
+public class UriConnegMediaTypeTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        Map<String, MediaType> mediaTypes = new HashMap<>();
+        mediaTypes.put("foo", MediaType.valueOf("application/foo"));
+        mediaTypes.put("bar", MediaType.valueOf("application/bar"));
+        mediaTypes.put("foot", MediaType.valueOf("application/foot"));
+
+        Set<Class<?>> classes = new HashSet<>();
+
+        for (Class<?> c : UriConnegMediaTypeTest.class.getClasses()) {
+            if (c.getAnnotation(Path.class) != null) {
+                classes.add(c);
+            }
+        }
+
+        ResourceConfig rc = new ResourceConfig(classes);
+        rc.property(ServerProperties.MEDIA_TYPE_MAPPINGS, mediaTypes);
+        return rc;
+    }
+
+    public abstract static class Base {
+        @GET
+        @Produces("application/foo")
+        public String doGetFoo(@Context HttpHeaders headers) {
+            assertEquals(1, headers.getAcceptableMediaTypes().size());
+            return "foo";
+        }
+
+        @GET
+        @Produces("application/foot")
+        public String doGetFoot() {
+            return "foot";
+        }
+
+        @GET
+        @Produces("application/bar")
+        public String doGetBar() {
+            return "bar";
+        }
+    }
+
+    @Path("/abc")
+    public static class SingleSegment extends Base {
+    }
+
+    @Path("/xyz/")
+    public static class SingleSegmentSlash extends Base {
+    }
+
+    @Path("/xyz/abc")
+    public static class MultipleSegments extends Base {
+    }
+
+    @Path("/xyz/xxx/")
+    public static class MultipleSegmentsSlash extends Base {
+    }
+
+    @Path("/xyz/abc.xml")
+    public static class DotPrefixSegments extends Base {
+    }
+
+    @Path("/foo_bar_foot")
+    public static class PathWithSuffixSegment extends Base {
+    }
+
+    @Path("/")
+    public static class SubResourceMethods extends Base {
+        @Path("sub")
+        @GET
+        @Produces("application/foo")
+        public String doGetFooS() {
+            return "foo";
+        }
+
+        @Path("sub")
+        @GET
+        @Produces("application/foot")
+        public String doGetFootS() {
+            return "foot";
+        }
+
+        @Path("sub")
+        @GET
+        @Produces("application/bar")
+        public String doGetBarS() {
+            return "bar";
+        }
+    }
+
+    @Test
+    public void testSlash() throws IOException {
+        _test("/");
+    }
+
+    @Test
+    public void testSingleSegment() throws IOException {
+        _test("/", "abc");
+    }
+
+    @Test
+    public void testSingleSegmentSlash() throws IOException {
+        _test("/", "xyz", "/");
+    }
+
+    @Test
+    public void testMultipleSegments() throws IOException {
+        _test("/xyz", "abc");
+    }
+
+    @Test
+    public void testMultipleSegmentsSlash() throws IOException {
+        _test("/xyz", "xxx", "/");
+    }
+
+    @Test
+    public void testDotPrefixSegments() throws IOException {
+        _test("/xyz", "abc.xml");
+        _test("/xyz", "abc", ".xml");
+    }
+
+    @Test
+    public void testXXXSegment() throws IOException {
+        _test("/", "foo_bar_foot");
+    }
+
+    @Test
+    public void testSubResourceMethods() throws IOException {
+        _test("/", "sub");
+    }
+
+    private void _test(String base) {
+        _test(base, "", "");
+    }
+
+    private void _test(String base, String path) {
+        _test(base, path, "");
+    }
+
+    private void _test(String base, String path, String terminate) {
+        WebTarget r = target().path(base);
+
+        String s = r.path(path + ".foo" + terminate).request().get(String.class);
+        assertEquals("foo", s);
+
+        s = r.path(path + ".foo" + terminate).request("application/bar").get(String.class);
+        assertEquals("foo", s);
+
+        s = r.path(path + ".foot" + terminate).request().get(String.class);
+        assertEquals("foot", s);
+
+        s = r.path(path + ".bar" + terminate).request().get(String.class);
+        assertEquals("bar", s);
+
+        s = r.path(path + terminate).request("application/foo").get(String.class);
+        assertEquals("foo", s);
+
+        s = r.path(path + terminate).request("application/foot").get(String.class);
+        assertEquals("foot", s);
+
+        s = r.path(path + terminate).request("application/foo;q=0.1").get(String.class);
+        assertEquals("foo", s);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ApplicationInfoTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ApplicationInfoTest.java
new file mode 100644
index 0000000..456b950
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ApplicationInfoTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2014, 2018 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.monitoring;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.ConstrainedTo;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+
+import javax.annotation.Priority;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.internal.spi.AutoDiscoverable;
+import org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.monitoring.ApplicationInfo;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * The test uses server properties {@link ServerProperties#MONITORING_STATISTICS_MBEANS_ENABLED},
+ * {@link ServerProperties#MONITORING_STATISTICS_MBEANS_ENABLED},
+ * {@link ServerProperties#MONITORING_STATISTICS_MBEANS_ENABLED}
+ * and it also implements {@link ForcedAutoDiscoverable} and tests if it is possible to inject
+ * {@link ApplicationInfo} in different circumstances.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ApplicationInfoTest extends JerseyTest {
+
+    private static final String FORCE_ENABLE = "FORCE_ENABLE";
+    private static final String ENABLE_MONITORING = "ENABLE_MONITORING";
+    private static final String ENABLE_MONITORING_STATISTICS = "ENABLE_MONITORING_STATISTICS";
+    private static final String ENABLE_MONITORING_STATISTICS_MBEANS = "ENABLE_MONITORING_STATISTICS_MBEANS";
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                //force, 3x AutoDiscoverable, 3x ResourceConfig,   response
+                // no property set => 500
+                {false, false, false, false, null, null, null, 500},
+                // property set by ForcedAutoDiscoverable => 200
+                {false, true, false, false, null, null, null, 200},
+                {false, false, true, false, null, null, null, 200},
+                {false, false, false, true, null, null, null, 200},
+                // property disable by ResourceConfig => 500
+                {false, true, false, false, false, false, false, 500},
+                {false, false, true, false, false, false, false, 500},
+                {false, false, false, true, false, false, false, 500},
+                // property disable by ResourceConfig but forced by ForcedAutoDiscoverable => 200
+                {true, true, false, false, false, false, false, 200},
+                {true, false, true, false, false, false, false, 200},
+                {true, false, false, true, false, false, false, 200}
+        });
+    }
+
+    private int responseStatus;
+
+    public ApplicationInfoTest(boolean forceEnable, boolean enableMonitoring,
+                               boolean enableMonitoringStatistics, boolean enableMonitoringStatisticsMBeans,
+                               Boolean monitoringEnabled, Boolean monitoringStatisticsEnabled,
+                               Boolean monitoringStatisticsMBeansEnabled, int responseStatus) {
+        super(createApplication(forceEnable, enableMonitoring,
+                enableMonitoringStatistics, enableMonitoringStatisticsMBeans,
+                monitoringEnabled, monitoringStatisticsEnabled,
+                monitoringStatisticsMBeansEnabled));
+        this.responseStatus = responseStatus;
+    }
+
+    private static Application createApplication(boolean forceEnable, boolean enableMonitoring,
+                                                 boolean enableMonitoringStatistics, boolean enableMonitoringStatisticsMBeans,
+                                                 Boolean monitoringEnabled, Boolean monitoringStatisticsEnabled,
+                                                 Boolean monitoringStatisticsMBeansEnabled) {
+        final ResourceConfig resourceConfig = new ResourceConfig(Resource.class);
+        resourceConfig.property(ServerProperties.APPLICATION_NAME, "testApp");
+        resourceConfig.property(FORCE_ENABLE, forceEnable);
+        if (enableMonitoring) {
+            resourceConfig.property(ENABLE_MONITORING, true);
+        }
+        if (enableMonitoringStatistics) {
+            resourceConfig.property(ENABLE_MONITORING_STATISTICS, true);
+        }
+        if (enableMonitoringStatisticsMBeans) {
+            resourceConfig.property(ENABLE_MONITORING_STATISTICS_MBEANS, true);
+        }
+        if (monitoringEnabled != null) {
+            resourceConfig.property(ServerProperties.MONITORING_ENABLED, monitoringEnabled);
+        }
+        if (monitoringStatisticsEnabled != null) {
+            resourceConfig.property(ServerProperties.MONITORING_STATISTICS_ENABLED, monitoringStatisticsEnabled);
+        }
+        if (monitoringStatisticsMBeansEnabled != null) {
+            resourceConfig.property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, monitoringStatisticsMBeansEnabled);
+        }
+
+        return resourceConfig;
+    }
+
+    @Test
+    public void test() {
+        final Response response = target().path("resource").request().get();
+        Assert.assertEquals(responseStatus, response.getStatus());
+        if (responseStatus == 200) {
+            Assert.assertEquals("testApp", response.readEntity(String.class));
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @Context
+        Provider<ApplicationInfo> applicationInfoProvider;
+
+        @GET
+        public String getAppName() {
+            final ApplicationInfo applicationInfo = applicationInfoProvider.get();
+            return applicationInfo.getResourceConfig().getApplicationName();
+        }
+    }
+
+    @ConstrainedTo(RuntimeType.SERVER)
+    @Priority(AutoDiscoverable.DEFAULT_PRIORITY - 1)
+    public static class ForcedAutoDiscoverableImpl implements ForcedAutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            final boolean forceEnable = PropertiesHelper.isProperty(context.getConfiguration().getProperty(FORCE_ENABLE));
+            if (PropertiesHelper.isProperty(context.getConfiguration().getProperty(ENABLE_MONITORING))) {
+                enable(context, forceEnable, ServerProperties.MONITORING_ENABLED);
+            }
+            if (PropertiesHelper.isProperty(context.getConfiguration().getProperty(ENABLE_MONITORING_STATISTICS))) {
+                enable(context, forceEnable, ServerProperties.MONITORING_STATISTICS_ENABLED);
+            }
+            if (PropertiesHelper.isProperty(context.getConfiguration().getProperty(ENABLE_MONITORING_STATISTICS_MBEANS))) {
+                enable(context, forceEnable, ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED);
+            }
+        }
+
+        private void enable(FeatureContext context, boolean forceEnable, String propertyName) {
+            if (forceEnable) {
+                context.property(propertyName, true);
+            } else {
+                if (context.getConfiguration().getProperty(propertyName) == null) {
+                    context.property(propertyName, true);
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/EventListenerTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/EventListenerTest.java
new file mode 100644
index 0000000..3bda9a9
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/EventListenerTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2013, 2018 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.monitoring;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.ResourceMethod;
+import org.glassfish.jersey.server.monitoring.ApplicationEvent;
+import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
+import org.glassfish.jersey.server.monitoring.RequestEvent;
+import org.glassfish.jersey.server.monitoring.RequestEventListener;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @author Miroslav Fuksa
+ */
+public class EventListenerTest extends JerseyTest {
+
+    private static final String APPLICATION_NAME = "MyApplication";
+    private static AppEventListener applicationEventListener;
+
+    @Override
+    protected Application configure() {
+        applicationEventListener = new AppEventListener();
+        final ResourceConfig resourceConfig = new ResourceConfig(MyResource.class);
+        resourceConfig.register(applicationEventListener);
+        resourceConfig.register(RequestFilter.class);
+        resourceConfig.register(PreMatchingRequestFilter.class);
+        resourceConfig.register(ResponseFilter.class);
+        resourceConfig.register(MyExceptionMapper.class);
+        resourceConfig.setApplicationName(APPLICATION_NAME);
+        return resourceConfig;
+    }
+
+    public static class AppEventListener implements ApplicationEventListener {
+        private ApplicationEvent appEventInitStart;
+        private ApplicationEvent appEventInitFinished;
+        private RequestEvent newRequestEvent;
+        private volatile int resourceMethodEventCount = 0;
+        private volatile CountDownLatch finishedCalled = new CountDownLatch(1);
+
+        @Override
+        public void onEvent(ApplicationEvent event) {
+            switch (event.getType()) {
+                case INITIALIZATION_START:
+                    this.appEventInitStart = event;
+                    break;
+                case INITIALIZATION_APP_FINISHED:
+                    this.appEventInitFinished = event;
+                    break;
+            }
+        }
+
+        @Override
+        public RequestEventListener onRequest(RequestEvent newRequestEvent) {
+            this.newRequestEvent = newRequestEvent;
+            if ("POST".equals(newRequestEvent.getContainerRequest().getMethod())) {
+                return null;
+            }
+
+            return new ReqEventListener(this);
+        }
+    }
+
+    public static class ReqEventListener implements RequestEventListener {
+        private final AppEventListener appEventListener;
+
+        public final MultivaluedMap<String, String> eventData = new MultivaluedHashMap<String, String>();
+
+        public ReqEventListener(AppEventListener appEventListener) {
+            this.appEventListener = appEventListener;
+        }
+
+        private int index = 1;
+
+        @Override
+        public void onEvent(RequestEvent event) {
+            switch (event.getType()) {
+                case REQUEST_MATCHED:
+                    eventData.add("R.REQ_FILTERS_START.order", String.valueOf(index++));
+                    break;
+                case REQUEST_FILTERED:
+                    eventData.add("R.REQ_FILTERS_FINISHED.order", String.valueOf(index++));
+                    break;
+                case LOCATOR_MATCHED:
+                    eventData.add("R.MATCHED_LOCATOR.order", String.valueOf(index++));
+                    final List<ResourceMethod> locators = event.getUriInfo().getMatchedResourceLocators();
+                    String msg = String.valueOf(locators.size())
+                            + ":" + locators.get(0).getInvocable().getHandlingMethod().getName();
+                    eventData.add("R.MATCHED_LOCATOR", msg);
+                    break;
+                case SUBRESOURCE_LOCATED:
+                    eventData.add("R.MATCHED_SUB_RESOURCE.order", String.valueOf(index++));
+                    break;
+                case RESOURCE_METHOD_START:
+                    eventData.add("R.RESOURCE_METHOD_START.order", String.valueOf(index++));
+                    this.appEventListener.resourceMethodEventCount++;
+                    final ResourceMethod resourceMethod = event.getUriInfo().getMatchedResourceMethod();
+                    eventData.add("R.RESOURCE_METHOD_START.method", resourceMethod
+                            .getInvocable().getHandlingMethod().getName());
+                    break;
+                case RESOURCE_METHOD_FINISHED:
+                    eventData.add("R.RESOURCE_METHOD_FINISHED.order", String.valueOf(index++));
+                    eventData.add("R.RESOURCE_METHOD_FINISHED", "ok");
+                    break;
+                case RESP_FILTERS_START:
+                    eventData.add("R.RESP_FILTERS_START.order", String.valueOf(index++));
+                    break;
+                case EXCEPTION_MAPPER_FOUND:
+                    eventData.add("R.EXCEPTION_MAPPER_FOUND.order", String.valueOf(index++));
+                    eventData.add("R.EXCEPTION_MAPPER_FOUND.exception", event.getException().getMessage());
+                    break;
+                case RESP_FILTERS_FINISHED:
+                    eventData.add("R.RESP_FILTERS_FINISHED.order", String.valueOf(index++));
+                    for (Map.Entry<String, List<String>> entry : eventData.entrySet()) {
+                        event.getContainerResponse().getHeaders().addAll(entry.getKey(), entry.getValue());
+                    }
+                    break;
+                case FINISHED:
+                    Assert.assertNotNull(event.getContainerResponse());
+                    this.appEventListener.finishedCalled.countDown();
+                    break;
+            }
+        }
+    }
+
+    public static class MyMappableException extends RuntimeException {
+        public MyMappableException(String message) {
+            super(message);
+        }
+    }
+
+    public static class MyExceptionMapper implements ExceptionMapper<MyMappableException> {
+
+        @Override
+        public Response toResponse(MyMappableException exception) {
+            return Response.ok("mapped").build();
+        }
+    }
+
+    public static class RequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+        }
+    }
+
+    @PreMatching
+    public static class PreMatchingRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+        }
+    }
+
+
+    public static class ResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        }
+    }
+
+    @Path("resource")
+    public static class MyResource {
+
+        @GET
+        public String getMethod() {
+            return "get";
+        }
+
+        @POST
+        public void post(String entity) {
+        }
+
+        @Path("locator")
+        public SubResource locator() {
+            return new SubResource();
+        }
+
+        @GET
+        @Path("async")
+        @ManagedAsync
+        public void getAsync(@Suspended AsyncResponse asyncResponse) {
+            asyncResponse.resume(Response.ok("async").build());
+        }
+
+        /**
+         * This works in the async way but it is served by only one thread.
+         * @param asyncResponse
+         */
+        @GET
+        @Path("asyncOneThread")
+        public void getAsyncOneThread(@Suspended AsyncResponse asyncResponse) {
+            asyncResponse.resume(Response.ok("async").build());
+        }
+    }
+
+
+    public static class SubResource {
+        @GET
+        public String get() {
+            return "sub";
+        }
+
+        @GET
+        @Path("exception")
+        public String getException() {
+            throw new MyMappableException("test-error");
+        }
+    }
+
+    @Test
+    public void testApplicationEvents() {
+        assertNotNull(applicationEventListener.appEventInitStart);
+        assertNotNull(applicationEventListener.appEventInitFinished);
+        assertEquals(APPLICATION_NAME, applicationEventListener.appEventInitStart.getResourceConfig().getApplicationName());
+        assertNull(applicationEventListener.newRequestEvent);
+        final Response response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+        assertNotNull(applicationEventListener.newRequestEvent);
+
+    }
+
+    @Test
+    public void testSimpleRequestEvent() {
+        assertEquals(0, applicationEventListener.resourceMethodEventCount);
+        assertNotNull(applicationEventListener.appEventInitStart);
+        assertNull(applicationEventListener.newRequestEvent);
+        Response response = target().path("resource").request().post(Entity.entity("entity",
+                MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(204, response.getStatus());
+        assertNotNull(applicationEventListener.newRequestEvent);
+        assertEquals(0, applicationEventListener.resourceMethodEventCount);
+        response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals(1, applicationEventListener.resourceMethodEventCount);
+    }
+
+    @Test
+    public void testMatchedLocator() {
+        final Response response = target().path("resource/locator").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("sub", response.readEntity(String.class));
+        assertEquals("[1:locator]", response.getHeaderString("R.MATCHED_LOCATOR"));
+    }
+
+    @Test
+    public void testMatchedMethod() {
+        final Response response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("get", response.readEntity(String.class));
+        assertEquals("[getMethod]", response.getHeaderString("R.RESOURCE_METHOD_START.method"));
+        assertEquals("[ok]", response.getHeaderString("R.RESOURCE_METHOD_FINISHED"));
+    }
+
+    @Test
+    public void testException() {
+        final Response response = target().path("resource/locator/exception").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("mapped", response.readEntity(String.class));
+        assertEquals("[org.glassfish.jersey.tests.e2e.server.monitoring.EventListenerTest$MyMappableException: test-error]",
+                response.getHeaderString("R.EXCEPTION_MAPPER_FOUND.exception"));
+    }
+
+
+    @Test
+    public void testSimpleProcessing() {
+        final Response response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+
+        assertEquals("get", response.readEntity(String.class));
+
+
+        int i = 1;
+        System.out.println(response.getHeaders());
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESP_FILTERS_START.order"));
+        assertEquals("[" + i + "]", response.getHeaderString("R.RESP_FILTERS_FINISHED.order"));
+    }
+
+
+    @Test
+    public void testLocatorProcessing() {
+        final Response response = target().path("resource/locator").request().get();
+        assertEquals(200, response.getStatus());
+
+        assertEquals("sub", response.readEntity(String.class));
+
+
+        int i = 1;
+        System.out.println(response.getHeaders());
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.MATCHED_LOCATOR.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.MATCHED_SUB_RESOURCE.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESP_FILTERS_START.order"));
+        assertEquals("[" + i + "]", response.getHeaderString("R.RESP_FILTERS_FINISHED.order"));
+    }
+
+    @Test
+    public void testExceptionProcessing() {
+        final Response response = target().path("resource/locator/exception").request().get();
+        assertEquals(200, response.getStatus());
+
+        assertEquals("mapped", response.readEntity(String.class));
+
+
+        int i = 1;
+        System.out.println(response.getHeaders());
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.MATCHED_LOCATOR.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.MATCHED_SUB_RESOURCE.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.EXCEPTION_MAPPER_FOUND.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESP_FILTERS_START.order"));
+        assertEquals("[" + i + "]", response.getHeaderString("R.RESP_FILTERS_FINISHED.order"));
+    }
+
+    @Test
+    public void testAsyncProcessing() throws InterruptedException {
+        final Response response = target().path("resource/async").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("async", response.readEntity(String.class));
+
+        int i = 1;
+        System.out.println(response.getHeaders());
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESP_FILTERS_START.order"));
+        assertEquals("[" + i + "]", response.getHeaderString("R.RESP_FILTERS_FINISHED.order"));
+        final boolean success = applicationEventListener.finishedCalled.await(3 * getAsyncTimeoutMultiplier(),
+                TimeUnit.SECONDS);
+        Assert.assertTrue(success);
+    }
+
+    @Test
+    public void testAsyncProcessingWithOneThread() throws InterruptedException {
+        final Response response = target().path("resource/asyncOneThread").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("async", response.readEntity(String.class));
+
+        int i = 1;
+        System.out.println(response.getHeaders());
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.REQ_FILTERS_FINISHED.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESOURCE_METHOD_START.order"));
+        assertEquals("[" + i++ + "]", response.getHeaderString("R.RESP_FILTERS_START.order"));
+        assertEquals("[" + i + "]", response.getHeaderString("R.RESP_FILTERS_FINISHED.order"));
+
+        final boolean success = applicationEventListener.finishedCalled.await(3 * getAsyncTimeoutMultiplier(),
+                TimeUnit.SECONDS);
+        Assert.assertTrue(success);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MBeansTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MBeansTest.java
new file mode 100644
index 0000000..85d0b97
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MBeansTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2013, 2018 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.monitoring;
+
+import java.lang.management.ManagementFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.server.monitoring.MonitoringStatisticsListener;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @author Miroslav Fuksa
+ */
+public class MBeansTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(TestResource.class, MyExceptionMapper.class);
+        resourceConfig.setApplicationName("myApplication");
+        resourceConfig.property("very-important", "yes");
+        resourceConfig.property("another-property", 48);
+        resourceConfig.property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, true);
+        resourceConfig.register(StatisticsListener.class);
+        return resourceConfig;
+    }
+
+    public static class MyException extends RuntimeException {
+
+        public MyException(String message) {
+            super(message);
+        }
+    }
+
+    public static class MyExceptionMapper implements ExceptionMapper<MyException> {
+
+        @Override
+        public Response toResponse(MyException exception) {
+            return Response.ok("mapped").build();
+        }
+    }
+
+    @Path("resource")
+    public static class TestResource {
+
+        @GET
+        public String testGet() {
+            return "get";
+        }
+
+        @GET
+        @Path("test/{test: \\d+}")
+        public String testGetPathPattern1() {
+            return "testGetPathPattern1";
+        }
+
+        @GET
+        @Path("test2/{test: hell?o}")
+        public String testGetPathPattern2() {
+            return "testGetPathPattern2";
+        }
+
+        @GET
+        @Path("test3/{test: abc.* (a)(b)[a,c]?$[1-4]kkx|Y}")
+        public String testGetPathPattern3() {
+            return "testGetPathPattern2";
+        }
+
+        @GET
+        @Path("test4/{test: [a,b]:r}")
+        public String testGetPathPattern4() {
+            return "testGetPathPattern2";
+        }
+
+        @POST
+        public String testPost() {
+            return "post";
+        }
+
+        @GET
+        @Path("sub")
+        public String testSubGet() {
+            return "sub";
+        }
+
+        @GET
+        @Path("exception")
+        public String testException() {
+            throw new MyException("test");
+        }
+
+        @POST
+        @Path("sub2")
+        @Produces("text/html")
+        @Consumes("text/plain")
+        public String testSu2bPost(String entity) {
+            return "post";
+        }
+
+        @Path("locator")
+        public SubResource getSubResource() {
+            return new SubResource();
+        }
+    }
+
+    public static class StatisticsListener extends AbstractContainerLifecycleListener implements MonitoringStatisticsListener {
+
+        public static boolean ON_SHUTDOWN_CALLED = false;
+
+        @Override
+        public void onStatistics(MonitoringStatistics statistics) {
+            // do nothing
+        }
+
+        @Override
+        public void onShutdown(Container container) {
+            StatisticsListener.ON_SHUTDOWN_CALLED = true;
+        }
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+        Assert.assertTrue(StatisticsListener.ON_SHUTDOWN_CALLED);
+
+    }
+
+    public static class SubResource {
+
+        @GET
+        @Path("in-subresource")
+        public String get() {
+            return "inSubResource";
+        }
+
+        @Path("locator")
+        public SubResource getSubResource() {
+            return new SubResource();
+        }
+    }
+
+    @Test
+    public void test() throws Exception {
+        final String path = "resource";
+        assertEquals(200, target().path(path).request().get().getStatus());
+        assertEquals(200, target().path(path).request().post(Entity.entity("post",
+                MediaType.TEXT_PLAIN_TYPE)).getStatus());
+        assertEquals(200, target().path(path).request().post(Entity.entity("post",
+                MediaType.TEXT_PLAIN_TYPE)).getStatus());
+        assertEquals(200, target().path(path).request().post(Entity.entity("post",
+                MediaType.TEXT_PLAIN_TYPE)).getStatus());
+        assertEquals(200, target().path(path).request().post(Entity.entity("post",
+                MediaType.TEXT_PLAIN_TYPE)).getStatus());
+        assertEquals(200, target().path(path + "/sub2").request().post(Entity.entity("post",
+                MediaType.TEXT_PLAIN_TYPE)).getStatus());
+        final Response response = target().path(path + "/exception").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("mapped", response.readEntity(String.class));
+
+        assertEquals(200, target().path("resource/sub").request().get().getStatus());
+        assertEquals(200, target().path("resource/sub").request().get().getStatus());
+        assertEquals(200, target().path("resource/locator/in-subresource").request().get().getStatus());
+        assertEquals(200, target().path("resource/locator/locator/in-subresource").request().get().getStatus());
+        assertEquals(200, target().path("resource/locator/locator/locator/in-subresource").request().get().getStatus());
+        assertEquals(404, target().path("resource/not-found-404").request().get().getStatus());
+
+        // wait until statistics are propagated to mxbeans
+        Thread.sleep(1500);
+
+        final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+        final ObjectName name = new ObjectName("org.glassfish.jersey:type=myApplication,subType=Global,global=Configuration");
+        final String str = (String) mBeanServer.getAttribute(name, "ApplicationName");
+        Assert.assertEquals("myApplication", str);
+
+        checkResourceMBean("/resource");
+        checkResourceMBean("/resource/sub");
+        checkResourceMBean("/resource/locator");
+        checkResourceMBean("/resource/exception");
+        checkResourceMBean("/resource/test/{test: \\\\d+}");
+        checkResourceMBean("/resource/test2/{test: hell\\?o}");
+        checkResourceMBean("/resource/test3/{test: abc.\\* (a)(b)[a,c]\\?$[1-4]kkx|Y}");
+        checkResourceMBean("/resource/test4/{test: [a,b]:r}");
+    }
+
+    private void checkResourceMBean(String name) throws MalformedObjectNameException {
+        final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+        final ObjectName objectName = new ObjectName(
+                "org.glassfish.jersey:type=myApplication,subType=Uris,resource=\"" + name + "\"");
+        ObjectInstance mbean = null;
+        try {
+            mbean = mBeanServer.getObjectInstance(objectName);
+        } catch (InstanceNotFoundException e) {
+            Assert.fail("Resource MBean name '" + name + "' not found.");
+        }
+        assertNotNull(mbean);
+    }
+
+    // this test runs the jersey environments, exposes mbeans and makes requests to
+    // the deployed application. The test will never finished. This should be uncommented
+    // only for development testing of mbeans in jconsole.
+    // Steps: uncomment the test; run it; run jconsole and attach to the process of the tests
+    //    @Test
+    //    public void testNeverFinishesAndMustBeCommented() throws Exception {
+    //        while (true) {
+    //            test();
+    //        }
+    //    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MonitoringStatisticsLocatorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MonitoringStatisticsLocatorTest.java
new file mode 100644
index 0000000..2a4dc66
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MonitoringStatisticsLocatorTest.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2013, 2018 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.monitoring;
+
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.model.ResourceMethod;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.server.monitoring.ResourceMethodStatistics;
+import org.glassfish.jersey.server.monitoring.ResourceStatistics;
+import org.glassfish.jersey.server.wadl.processor.WadlModelProcessor;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This test verifies that {@link ResourceMethodStatistics} are not duplicated in
+ * {@link MonitoringStatistics} when sub resource locators are used. Sub resources and their
+ * methods should be mapped to currently existing {@link ResourceStatistics} and their
+ * {@link ResourceMethodStatistics}.
+ *
+ * @author Miroslav Fuksa
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class MonitoringStatisticsLocatorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(StatisticsResource.class, AnotherResource.class);
+        resourceConfig.property(ServerProperties.MONITORING_STATISTICS_ENABLED, true);
+        resourceConfig.property(ServerProperties.APPLICATION_NAME, "testApp");
+        return resourceConfig;
+    }
+
+    @Path("resource")
+    public static class StatisticsResource {
+
+        @Context
+        Provider<MonitoringStatistics> statistics;
+
+        @GET
+        public String getStats() throws InterruptedException {
+            final MonitoringStatistics monitoringStatistics = statistics.get();
+            final ResourceStatistics resourceStatistics = monitoringStatistics.getResourceClassStatistics()
+                    .get(SubResource.class);
+            if (resourceStatistics == null) {
+                return "null";
+            }
+
+            String resp = "";
+
+            for (final Map.Entry<ResourceMethod, ResourceMethodStatistics> entry
+                    : resourceStatistics.getResourceMethodStatistics().entrySet()) {
+                if (entry.getKey().getHttpMethod().equals("GET")) {
+                    resp = resp + "getFound";
+                }
+            }
+            return resp;
+        }
+
+        @GET
+        @Path("uri")
+        public String getUriStats() throws InterruptedException {
+            final MonitoringStatistics monitoringStatistics = statistics.get();
+            final ResourceStatistics resourceStatistics = monitoringStatistics.getUriStatistics()
+                    .get("/resource/resource-locator");
+            if (resourceStatistics == null) {
+                return "null";
+            }
+
+            String resp = "";
+
+            for (final Map.Entry<ResourceMethod, ResourceMethodStatistics> entry
+                    : resourceStatistics.getResourceMethodStatistics().entrySet()) {
+                if (entry.getKey().getHttpMethod().equals("GET")) {
+                    resp = resp + "getFound";
+                }
+            }
+
+            return resp;
+        }
+
+        @Path("resource-locator")
+        public SubResource locator() {
+            return new SubResource();
+        }
+
+        @Path("hello")
+        @GET
+        @Produces("text/plain")
+        public String hello() {
+            return "Hello!";
+        }
+
+        @GET
+        @Path("resourceClassStatisticsWadlOptionsTest")
+        public String getResourceClassStatisticsWadlOptionsTest() {
+            return getResourceClassStatisticsTest(WadlModelProcessor.OptionsHandler.class.getName());
+        }
+
+        @GET
+        @Path("resourceClassStatisticsGenericOptionsTest")
+        public String getResourceClassStatisticsGenericOptionsTest() {
+            return getResourceClassStatisticsTest(
+                    "org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor$GenericOptionsInflector");
+        }
+
+        @GET
+        @Path("resourceClassStatisticsPlainTextOptionsTest")
+        public String getResourceClassStatisticsPlainTestOptionsTest() {
+            return getResourceClassStatisticsTest(
+                    "org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor$PlainTextOptionsInflector");
+        }
+
+        private String getResourceClassStatisticsTest(final String resourceClassName) {
+            final ResourceStatistics resourceMethodStatistics = findResourceClassStatistics(statistics.get(), resourceClassName);
+
+            boolean resourceHelloOptions = false;
+            boolean anotherHelloOptions = false;
+            boolean anotherXmlOptions = false;
+            for (final Map.Entry<ResourceMethod, ResourceMethodStatistics> entry : resourceMethodStatistics
+                    .getResourceMethodStatistics().entrySet()) {
+                final ResourceMethod resourceMethod = entry.getKey();
+                final String fullPath = getFullPath(resourceMethod);
+                if ("/resource/hello".equals(fullPath)) {
+                    resourceHelloOptions = true;
+                } else if ("/another/hello".equals(fullPath)) {
+                    anotherHelloOptions = true;
+                } else if ("/another/xml".equals(fullPath)) {
+                    anotherXmlOptions = true;
+                }
+            }
+            if (resourceHelloOptions && anotherHelloOptions && anotherXmlOptions) {
+                return "OK";
+            } else {
+                return "FAIL: /resource/hello=" + resourceHelloOptions + "; /another/hello=" + anotherHelloOptions
+                        + "; /another/xml=" + anotherXmlOptions;
+            }
+        }
+
+        @GET
+        @Path("uriStatisticsResourceHelloTest")
+        public String getUriStatisticsResourceHelloTest() {
+            return getUriStatisticsTest("/resource/hello");
+        }
+
+        @GET
+        @Path("uriStatisticsAnotherHelloTest")
+        public String getUriStatisticsAnotherHelloTest() {
+            return getUriStatisticsTest("/another/hello");
+        }
+
+        @GET
+        @Path("uriStatisticsAnotherXmlTest")
+        public String getUriStatisticsAnotherXmlTest() {
+            return getUriStatisticsTest("/another/xml");
+        }
+
+        private String getUriStatisticsTest(final String uri) {
+            boolean plainTextOptions = false;
+            boolean wadlOptions = false;
+            boolean genericOptions = false;
+            final ResourceStatistics resourceStatistics = statistics.get().getUriStatistics().get(uri);
+
+            for (final Map.Entry<ResourceMethod, ResourceMethodStatistics> entry : resourceStatistics
+                    .getResourceMethodStatistics().entrySet()) {
+                if (entry.getKey().getHttpMethod().equals("OPTIONS")) {
+                    final ResourceMethod resourceMethod = entry.getKey();
+                    final String producedTypes = resourceMethod.getProducedTypes().toString();
+                    if ("[text/plain]".equals(producedTypes)) {
+                        plainTextOptions = true;
+                    } else if ("[application/vnd.sun.wadl+xml]".equals(producedTypes)) {
+                        wadlOptions = true;
+                    } else if ("[*/*]".equals(producedTypes)) {
+                        genericOptions = true;
+                    }
+                }
+            }
+            if (plainTextOptions && wadlOptions && genericOptions) {
+                return "OK";
+            } else {
+                return "FAIL: [text/plain]=" + plainTextOptions + "; [application/vnd.sun.wadl+xml]=" + wadlOptions
+                        + "; [*/*]=" + genericOptions;
+            }
+        }
+
+        private ResourceStatistics findResourceClassStatistics(final MonitoringStatistics monitoringStatistics,
+                                                               final String resourceClassName) {
+            for (final Map.Entry<Class<?>, ResourceStatistics> entry : monitoringStatistics.getResourceClassStatistics()
+                    .entrySet()) {
+                final Class<?> key = entry.getKey();
+                final String clazz = key.getName();
+
+                if (clazz.equals(resourceClassName)) {
+                    return entry.getValue();
+                }
+            }
+            return null;
+        }
+
+        private static String getFullPath(final ResourceMethod resourceMethod) {
+            final StringBuilder fullPath = new StringBuilder();
+            if (resourceMethod != null) {
+                prefixPath(fullPath, resourceMethod.getParent());
+            }
+            return fullPath.toString();
+        }
+
+        private static void prefixPath(final StringBuilder fullPath, final Resource parent) {
+            if (parent != null) {
+                String path = parent.getPath();
+                if (path.startsWith("/")) {
+                    path = path.substring(1);
+                }
+                fullPath.insert(0, "/" + path);
+                prefixPath(fullPath, parent.getParent());
+            }
+        }
+
+    }
+
+    public static class SubResource {
+
+        @GET
+        public String get() {
+            return "get";
+        }
+
+        @Path("sub")
+        public SubResource subLocator() {
+            return new SubResource();
+        }
+
+    }
+
+    @Path("/another")
+    public static class AnotherResource {
+
+        @Path("hello")
+        @GET
+        @Produces("text/plain")
+        public String sayHello() {
+            return "Hello, again.";
+        }
+
+        @Path("xml")
+        @GET
+        @Produces(MediaType.TEXT_XML)
+        public String sayXMLHello() {
+            return "<?xml version=\"1.0\"?><hello>World!</hello>";
+        }
+    }
+
+    @Test
+    public void test() throws InterruptedException {
+        Response response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("null", response.readEntity(String.class));
+
+        response = target().path("resource/resource-locator").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("get", response.readEntity(String.class));
+
+        response = target().path("resource/resource-locator").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("get", response.readEntity(String.class));
+
+        response = target().path("resource/resource-locator/sub").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("get", response.readEntity(String.class));
+
+        response = target().path("resource/hello").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Hello!", response.readEntity(String.class));
+
+        response = target().path("another/hello").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Hello, again.", response.readEntity(String.class));
+
+        response = target().path("another/xml").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("<?xml version=\"1.0\"?><hello>World!</hello>", response.readEntity(String.class));
+
+        Thread.sleep(600);
+
+        response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("getFound", response.readEntity(String.class));
+
+        response = target().path("resource/uri").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("getFound", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceClassStatisticsWadlOptions() {
+        final Response response = target().path("resource/resourceClassStatisticsWadlOptionsTest").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceClassStatisticsGenericOptions() {
+        final Response response = target().path("resource/resourceClassStatisticsGenericOptionsTest").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceClassStatisticsPlainTextOptions() {
+        final Response response = target().path("resource/resourceClassStatisticsPlainTextOptionsTest").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testUriStatisticsResourceHello() throws InterruptedException {
+        Response response = target().path("resource/hello").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Hello!", response.readEntity(String.class));
+
+        Thread.sleep(600);
+
+        response = target().path("resource/uriStatisticsResourceHelloTest").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testUriStatisticsAnotherHello() throws InterruptedException {
+        Response response = target().path("another/hello").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Hello, again.", response.readEntity(String.class));
+
+        Thread.sleep(600);
+
+        response = target().path("resource/uriStatisticsAnotherHelloTest").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testUriStatisticsAnotherXml() throws InterruptedException {
+        Response response = target().path("another/xml").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("<?xml version=\"1.0\"?><hello>World!</hello>", response.readEntity(String.class));
+
+        Thread.sleep(600);
+
+        response = target().path("resource/uriStatisticsAnotherXmlTest").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ReloadApplicationEventTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ReloadApplicationEventTest.java
new file mode 100644
index 0000000..a7a3697
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ReloadApplicationEventTest.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2013, 2018 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.monitoring;
+
+import java.lang.management.ManagementFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.monitoring.ApplicationEvent;
+import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
+import org.glassfish.jersey.server.monitoring.RequestEvent;
+import org.glassfish.jersey.server.monitoring.RequestEventListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This test tests the lifecycle of the application in accordance to the monitoring
+ * and {@link ContainerLifecycleListener container events}. Among others, it checks that
+ * Monitoring MBeans are correctly exposed and destroyed when application is reloaded. Uses different
+ * containers to test it.
+ *
+ * @author Miroslav Fuksa
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({ReloadApplicationEventTest.GrizzlyTestCase.class, ReloadApplicationEventTest.JdkServerTestCase.class,
+        ReloadApplicationEventTest.SimpleHttpServerTestCase.class})
+public class ReloadApplicationEventTest extends JerseyTest {
+
+    public static final String ORIGINAL = "original";
+    public static final String RELOADED = "reloaded";
+
+    public static class GrizzlyTestCase extends ParentTest {
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new GrizzlyTestContainerFactory();
+        }
+    }
+
+    public static class JdkServerTestCase extends ParentTest {
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new JdkHttpServerTestContainerFactory();
+        }
+    }
+
+    /**
+     * Works only with Java 7
+     */
+    public static class JettyServerTestCase extends ParentTest {
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new JettyTestContainerFactory();
+        }
+    }
+
+    public static class SimpleHttpServerTestCase extends ParentTest {
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new SimpleTestContainerFactory();
+        }
+    }
+
+    public static class ParentTest extends JerseyTest {
+
+        @Override
+        public void setUp() throws Exception {
+            super.setUp();
+        }
+
+        @Override
+        protected Application configure() {
+            OriginalResult.reset();
+            ReloadedResult.reset();
+            OriginalResult originalResult = new OriginalResult();
+            final ResourceConfig resourceConfig = getResourceConfig()
+                    .setApplicationName(ORIGINAL)
+                    .register(new TestResource(originalResult))
+                    .register(new AppEventListener(originalResult));
+
+            return resourceConfig;
+        }
+
+        private static ResourceConfig getResourceConfig() {
+            final ResourceConfig resourceConfig = new ResourceConfig();
+            resourceConfig.property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, true);
+            return resourceConfig;
+        }
+
+        public static interface TestResultTracker {
+
+            public void reloaded();
+
+            public void shutdown();
+
+            public void startup();
+
+            /**
+             * {@link ApplicationEvent.Type#RELOAD_FINISHED} called.
+             */
+            public void reloadedEvent();
+
+            /**
+             * {@link ApplicationEvent.Type#INITIALIZATION_FINISHED} called.
+             */
+            public void initEvent();
+        }
+
+        public static class OriginalResult implements TestResultTracker {
+
+            public static boolean reloadedCalled;
+            public static boolean shutdownCalled;
+            public static boolean startupCalled;
+            public static boolean reloadedEventCalled;
+            public static boolean initEventCalled;
+
+            public static void reset() {
+                reloadedCalled = false;
+                shutdownCalled = false;
+                startupCalled = false;
+                reloadedEventCalled = false;
+                initEventCalled = false;
+            }
+
+            @Override
+            public void reloaded() {
+                reloadedCalled = true;
+            }
+
+            @Override
+            public void shutdown() {
+                shutdownCalled = true;
+            }
+
+            @Override
+            public void startup() {
+                startupCalled = true;
+            }
+
+            @Override
+            public void reloadedEvent() {
+                reloadedEventCalled = true;
+            }
+
+            @Override
+            public void initEvent() {
+                initEventCalled = true;
+            }
+
+        }
+
+        public static class ReloadedResult implements TestResultTracker {
+
+            public static boolean shutdownCalled;
+            public static boolean reloadedCalled;
+            public static boolean reloadedEventCalled;
+            public static boolean initEventCalled;
+            public static boolean startupCalled;
+
+            public static void reset() {
+                reloadedCalled = false;
+                shutdownCalled = false;
+                startupCalled = false;
+                reloadedEventCalled = false;
+                initEventCalled = false;
+            }
+
+            @Override
+            public void reloaded() {
+                reloadedCalled = true;
+
+            }
+
+            @Override
+            public void shutdown() {
+                shutdownCalled = true;
+            }
+
+            @Override
+            public void startup() {
+                startupCalled = true;
+            }
+
+            @Override
+            public void reloadedEvent() {
+                reloadedEventCalled = true;
+            }
+
+            @Override
+            public void initEvent() {
+                initEventCalled = true;
+            }
+
+        }
+
+        @Path("resource")
+        @Singleton
+        public static class TestResource implements ContainerLifecycleListener {
+
+            private volatile Container container;
+
+            private final TestResultTracker testResultTracker;
+
+            public TestResource(TestResultTracker testResultTracker) {
+                this.testResultTracker = testResultTracker;
+            }
+
+            @GET
+            public String get() {
+                container.reload(getResourceConfig()
+                        .setApplicationName(RELOADED)
+                        .register(new TestResource(new ReloadedResult()))
+                        .register(new AppEventListener(new ReloadedResult())));
+                return "get";
+            }
+
+            @Override
+            public void onStartup(Container container) {
+                this.container = container;
+                testResultTracker.startup();
+            }
+
+            @Override
+            public void onReload(Container container) {
+                testResultTracker.reloaded();
+            }
+
+            @Override
+            public void onShutdown(Container container) {
+                testResultTracker.shutdown();
+            }
+        }
+
+        public static class AppEventListener implements ApplicationEventListener {
+
+            private final TestResultTracker resultTracker;
+
+            public AppEventListener(TestResultTracker resultTracker) {
+                this.resultTracker = resultTracker;
+            }
+
+            @Override
+            public void onEvent(ApplicationEvent event) {
+                switch (event.getType()) {
+                    case INITIALIZATION_FINISHED:
+                        resultTracker.initEvent();
+                        break;
+                    case RELOAD_FINISHED:
+                        resultTracker.reloadedEvent();
+                        break;
+                }
+            }
+
+            @Override
+            public RequestEventListener onRequest(RequestEvent requestEvent) {
+                return null;
+            }
+        }
+
+        /**
+         * Tests that monitoring and container events are correctly called when application is created and redeployed.
+         * It also checks that MBeans are exposed and deregistered when the application is undeployed. Test contains
+         * waits and timeouts as monitoring events are processed asynchronously and it might take some time
+         * until MBeans are registered. The test deploys original application, then reload is initiated and
+         * another application is deployed.
+         *
+         * @throws MalformedObjectNameException
+         * @throws AttributeNotFoundException
+         * @throws MBeanException
+         * @throws ReflectionException
+         * @throws InstanceNotFoundException
+         * @throws InterruptedException
+         */
+        @Test
+        public void testApplicationEvents() throws MalformedObjectNameException, AttributeNotFoundException,
+                MBeanException, ReflectionException, InstanceNotFoundException, InterruptedException {
+            // wait to expose MBeans in the ORIGINAL application
+            Thread.sleep(700);
+
+            // before reload
+            assertTrue(OriginalResult.startupCalled);
+            assertTrue(OriginalResult.initEventCalled);
+            assertFalse(OriginalResult.shutdownCalled);
+
+            assertFalse(ReloadedResult.reloadedCalled);
+            assertFalse(ReloadedResult.startupCalled);
+            assertFalse(ReloadedResult.initEventCalled);
+
+            checkMBeanRegistration(ORIGINAL, true);
+            checkMBeanRegistration(RELOADED, false);
+
+            // now cause the reload:
+            final Response response = target().path("resource").request().get();
+            assertEquals(200, response.getStatus());
+
+            int cnt = 0;
+            while ((!ReloadedResult.initEventCalled) && (cnt++ < 30)) {
+                Thread.sleep(200);
+            }
+            assertTrue("Timeout: application was not reloaded in time.", ReloadedResult.initEventCalled);
+            // wait again some time until events are processed and mbeans are invoked
+            Thread.sleep(700);
+
+            // after reload
+            assertFalse(OriginalResult.reloadedCalled);
+            assertFalse(OriginalResult.reloadedEventCalled);
+            assertTrue(OriginalResult.shutdownCalled);
+            assertTrue(ReloadedResult.startupCalled);
+            assertTrue(ReloadedResult.initEventCalled);
+            assertTrue(ReloadedResult.reloadedCalled);
+            assertTrue(ReloadedResult.reloadedEventCalled);
+
+            checkMBeanRegistration(ORIGINAL, false);
+            checkMBeanRegistration(RELOADED, true);
+        }
+
+        private void checkMBeanRegistration(String appName, boolean shouldBeRegistered)
+                throws MalformedObjectNameException,
+                MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException,
+                InterruptedException {
+
+            MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+            final ObjectName name = new ObjectName(
+                    "org.glassfish.jersey:type=" + appName + ",subType=Global,global=Configuration");
+            boolean registered = mBeanServer.isRegistered(name);
+
+            int time = 0;
+            while (shouldBeRegistered && !registered && time < 4000) {
+                // wait until MBeans are asynchronously exposed
+                int waitTime = 300;
+                time += waitTime;
+                Thread.sleep(waitTime);
+                registered = mBeanServer.isRegistered(name);
+            }
+            Assert.assertEquals(shouldBeRegistered, registered);
+            if (registered) {
+                final String str = (String) mBeanServer.getAttribute(name, "ApplicationName");
+                Assert.assertEquals(appName, str);
+            }
+        }
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/StatisticsDestroyTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/StatisticsDestroyTest.java
new file mode 100644
index 0000000..1fa5d76
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/StatisticsDestroyTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2013, 2018 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.monitoring;
+
+import java.lang.management.ManagementFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.monitoring.DestroyListener;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.server.monitoring.MonitoringStatisticsListener;
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ * @author Miroslav Fuksa
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({StatisticsDestroyTest.GrizzlyTestCase.class, StatisticsDestroyTest.JdkServerTestCase.class,
+        StatisticsDestroyTest.SimpleHttpServerTestCase.class})
+public class StatisticsDestroyTest {
+
+    public static class ParentTest extends JerseyTest {
+        @Override
+        public Application configure() {
+            StatisticsListener.reset();
+            final ResourceConfig resourceConfig = new ResourceConfig(TestResource.class);
+            resourceConfig.setApplicationName("myApplication");
+            resourceConfig.property("very-important", "yes");
+            resourceConfig.property("another-property", 48);
+            resourceConfig.property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, true);
+            resourceConfig.register(StatisticsListener.class);
+            return resourceConfig;
+        }
+
+        @Override
+        @After
+        public void tearDown() throws Exception {
+            super.tearDown();
+            assertTrue(StatisticsListener.ON_SHUTDOWN_CALLED);
+            assertTrue(StatisticsListener.ON_DESTROY_CALLED);
+            assertTrue(StatisticsListener.ON_STATISTICS_CALLED);
+
+            final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+            final ObjectName name = new ObjectName("org.glassfish.jersey:type=myApplication,subType=Global,global=Configuration");
+            boolean registered = mBeanServer.isRegistered(name);
+            int time = 0;
+            while (registered && time < 4000) {
+                // wait until MBeans are asynchronously exposed
+                int waitTime = 300;
+                time += waitTime;
+                Thread.sleep(waitTime);
+                registered = mBeanServer.isRegistered(name);
+            }
+
+            Assert.assertFalse("MBean should be already unregistered!", mBeanServer.isRegistered(name));
+        }
+
+        @Path("resource")
+        public static class TestResource {
+            @GET
+            public String testGet() {
+                return "get";
+            }
+        }
+
+        @Test
+        public void test() throws Exception {
+            final String path = "resource";
+            assertEquals(200, target().path(path).request().get().getStatus());
+            final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+            final ObjectName name = new ObjectName("org.glassfish.jersey:type=myApplication,subType=Global,global=Configuration");
+            boolean registered = mBeanServer.isRegistered(name);
+
+            // wait (events are processed asynchronously and it might take time to expose mbeans
+            int time = 0;
+            while (!registered && time < 4000) {
+                // wait until MBeans are asynchronously exposed
+                int waitTime = 300;
+                time += waitTime;
+                Thread.sleep(waitTime);
+                registered = mBeanServer.isRegistered(name);
+            }
+
+            assertTrue("MBean should be already registered!", mBeanServer.isRegistered(name));
+            final String str = (String) mBeanServer.getAttribute(name, "ApplicationName");
+            Assert.assertEquals("myApplication", str);
+        }
+    }
+
+    public static class GrizzlyTestCase extends ParentTest {
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            StatisticsListener.reset();
+            return new GrizzlyTestContainerFactory();
+        }
+    }
+
+    public static class JdkServerTestCase extends ParentTest {
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            StatisticsListener.reset();
+            return new JdkHttpServerTestContainerFactory();
+        }
+    }
+
+    /**
+     * Works only with Java 7
+     */
+    public static class JettyServerTestCase extends ParentTest {
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            StatisticsListener.reset();
+            return new JettyTestContainerFactory();
+        }
+    }
+
+    public static class SimpleHttpServerTestCase extends ParentTest {
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            StatisticsListener.reset();
+            return new SimpleTestContainerFactory();
+        }
+    }
+
+    public static class StatisticsListener extends AbstractContainerLifecycleListener
+            implements MonitoringStatisticsListener, DestroyListener {
+
+        public static boolean ON_SHUTDOWN_CALLED = false;
+        public static boolean ON_STATISTICS_CALLED = false;
+        public static boolean ON_DESTROY_CALLED = false;
+
+        public static void reset() {
+            ON_SHUTDOWN_CALLED = false;
+            ON_STATISTICS_CALLED = false;
+            ON_DESTROY_CALLED = false;
+        }
+
+        @Override
+        public void onStatistics(MonitoringStatistics statistics) {
+            StatisticsListener.ON_STATISTICS_CALLED = true;
+        }
+
+        @Override
+        public void onShutdown(Container container) {
+            StatisticsListener.ON_SHUTDOWN_CALLED = true;
+        }
+
+        @Override
+        public void onDestroy() {
+            StatisticsListener.ON_DESTROY_CALLED = true;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest.java
new file mode 100644
index 0000000..9558bb6
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.ErrorTemplate;
+import org.glassfish.jersey.server.mvc.beanvalidation.MvcBeanValidationFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.hibernate.validator.constraints.Length;
+import org.junit.Before;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Michal Gajdos
+ */
+public class BeanValidationErrorTemplateTest extends JerseyTest {
+
+    private Properties props;
+
+    @Before
+    public void setUp() throws Exception {
+        props = new Properties();
+
+        super.setUp();
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ErrorTemplateResource.class)
+                .register(MvcBeanValidationFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    @Path("/")
+    @Consumes("text/plain")
+    public static class ErrorTemplateResource {
+
+        @POST
+        @Path("params")
+        @ErrorTemplate
+        public String invalidParams(@Length(min = 5) final String value) {
+            fail("Should fail on Bean Validation!");
+            return value;
+        }
+
+        @POST
+        @Path("return")
+        @ErrorTemplate
+        @Length(min = 5)
+        public String invalidReturnValue(final String value) {
+            return value;
+        }
+    }
+
+    @Test
+    public void testInvalidParams() throws Exception {
+        final Response response = target("params").request().post(Entity.text("foo"));
+        props.load(response.readEntity(InputStream.class));
+
+        assertThat(response.getStatus(), equalTo(400));
+        assertThat(props.getProperty("model"),
+                equalTo("{org.hibernate.validator.constraints.Length.message}_ErrorTemplateResource.invalidParams.arg0_foo"));
+    }
+
+    @Test
+    public void testInvalidReturnValue() throws Exception {
+        final Response response = target("return").request().post(Entity.text("foo"));
+        props.load(response.readEntity(InputStream.class));
+
+        assertThat(response.getStatus(), equalTo(500));
+        assertThat(props.getProperty("model"),
+                equalTo("{org.hibernate.validator.constraints.Length.message}_ErrorTemplateResource.invalidReturnValue."
+                        + "<return value>_foo"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/CustomViewableContextTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/CustomViewableContextTest.java
new file mode 100644
index 0000000..afcb4d8
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/CustomViewableContextTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.CustomViewableContext;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class CustomViewableContextTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ExplicitTemplate.class, ImplicitTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class)
+                .register(CustomViewableContext.class);
+    }
+
+    @Path("/explicit")
+    public static class ExplicitTemplate {
+
+        @GET
+        @Template
+        public Viewable getViewable() {
+            return new Viewable("", "method");
+        }
+
+        @GET
+        @Path("annotation")
+        @Template
+        public String getTemplate() {
+            return "annotation";
+        }
+    }
+
+    @Template
+    @Path("/implicit")
+    public static class ImplicitTemplate {
+
+        @Override
+        public String toString() {
+            return "implicit";
+        }
+    }
+
+    @Test
+    public void testExplicitMethod() throws Exception {
+        testResource("explicit", "method");
+    }
+
+    @Test
+    public void testExplicitAnnotation() throws Exception {
+        testResource("explicit/annotation", "annotation");
+    }
+
+    @Test
+    public void testImplicit() throws Exception {
+        testResource("implicit", "implicit");
+    }
+
+    private void testResource(final String path, final String modelValue) throws Exception {
+        final Properties p = new Properties();
+        p.load(target(path).request().get(InputStream.class));
+
+        assertEquals("/CustomViewableContext/index.testp", p.getProperty("path"));
+        assertEquals(modelValue, p.getProperty("model"));
+        assertEquals("TestViewProcessor", p.getProperty("name"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest.java
new file mode 100644
index 0000000..ef8475a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.ErrorTemplate;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ErrorTemplateTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ErrorTemplateResource.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    @Path("/")
+    public static class ErrorTemplateResource {
+
+        @GET
+        @ErrorTemplate
+        public String method() {
+            throw new RuntimeException("ErrorTemplate");
+        }
+
+        @GET
+        @Path("methodRelativePath")
+        @ErrorTemplate(name = "relative")
+        public String methodRelativePath() {
+            throw new RuntimeException("ErrorTemplate");
+        }
+
+        @GET
+        @Path("methodAbsolutePath")
+        @ErrorTemplate(name = "/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute")
+        public String methodAbsolutePath() {
+            throw new RuntimeException("ErrorTemplate");
+        }
+
+        @Path("subResource")
+        public ErrorTemplateResource subResource() {
+            return new ErrorTemplateResource();
+        }
+
+        @Path("subResourceTemplate")
+        @ErrorTemplate
+        public ErrorTemplateSubResource subResourceTemplate() {
+            return new ErrorTemplateSubResource();
+        }
+    }
+
+    public static class ErrorTemplateSubResource {
+
+        @GET
+        public String get() {
+            throw new RuntimeException("ErrorTemplate");
+        }
+    }
+
+    @Test(expected = InternalServerErrorException.class)
+    public void testErrorMethodTemplateSubResource() throws Exception {
+        target("subResourceTemplate").request().get(String.class);
+    }
+
+    @Test
+    public void testErrorTemplate() throws Exception {
+        testErrorTemplate(target());
+    }
+
+    @Test
+    public void testErrorTemplateSubResource() throws Exception {
+        testErrorTemplate(target("subResource"));
+    }
+
+    private void testErrorTemplate(final WebTarget target) throws Exception {
+        Properties props = new Properties();
+        props.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/index.testp",
+                props.getProperty("path"));
+        assertEquals("java.lang.RuntimeException: ErrorTemplate", props.getProperty("model"));
+
+        props = new Properties();
+        props.load(target.path("methodRelativePath").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/relative.testp",
+                props.getProperty("path"));
+        assertEquals("java.lang.RuntimeException: ErrorTemplate", props.getProperty("model"));
+
+        props = new Properties();
+        props.load(target.path("methodAbsolutePath").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.testp",
+                props.getProperty("path"));
+        assertEquals("java.lang.RuntimeException: ErrorTemplate", props.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest.java
new file mode 100644
index 0000000..772c099
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class ExceptionViewProcessorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ExplicitTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class)
+                .register(WebAppExceptionMapper.class);
+    }
+
+    @Provider
+    public static class WebAppExceptionMapper implements ExceptionMapper<WebApplicationException> {
+
+        public Response toResponse(WebApplicationException exception) {
+            // Absolute.
+            if (exception.getResponse().getStatus() == 404) {
+                return Response
+                        .status(404)
+                        .entity(new Viewable("/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/404", "404"))
+                        .build();
+            }
+
+            // Relative.
+            if (exception.getResponse().getStatus() == 406) {
+                return Response.status(406).entity(
+                        new Viewable(
+                                "/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/WebAppExceptionMapper/406",
+                                "406")).build();
+            }
+
+            return exception.getResponse();
+        }
+    }
+
+    @Path("/")
+    public static class ExplicitTemplate {
+
+        @GET
+        @Produces("application/foo")
+        public Viewable get() {
+            return new Viewable("show", "get");
+        }
+    }
+
+    @Test
+    public void testAbsoluteExplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("/does-not-exist").request();
+
+        Response cr = request.get(Response.class);
+        assertEquals(404, cr.getStatus());
+
+        Properties p = new Properties();
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/404.testp", p.getProperty("path"));
+        assertEquals("404", p.getProperty("model"));
+    }
+
+    @Test
+    public void testResolvingClassExplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("/").request("application/wrong-media-type");
+
+        Response cr = request.get(Response.class);
+        assertEquals(406, cr.getStatus());
+
+        Properties p = new Properties();
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/WebAppExceptionMapper/406.testp",
+                p.getProperty("path"));
+        assertEquals("406", p.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest.java
new file mode 100644
index 0000000..95d970b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.AbcViewProcessor;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.DefViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ExplicitProduceTemplateTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ExplicitTwoGetProducesTemplate.class,
+                ExplicitNoProducesTemplate.class, ExplicitWildcardProducesTemplate.class, ExplicitTemplateProducesClass.class)
+                .register(MvcFeature.class)
+                .register(AbcViewProcessor.class)
+                .register(DefViewProcessor.class);
+    }
+
+    @Path("/explicit-no-produces")
+    public static class ExplicitNoProducesTemplate {
+
+        @GET
+        @Template
+        public String def() {
+            return "def";
+        }
+    }
+
+    @Path("/explicit-wildcard-produces")
+    public static class ExplicitWildcardProducesTemplate {
+
+        @GET
+        @Template
+        @Produces("*/*")
+        public String def() {
+            return "def";
+        }
+    }
+
+    @Path("/explicit-two-get-produces")
+    public static class ExplicitTwoGetProducesTemplate {
+
+        @GET
+        @Template
+        @Produces("application/abc")
+        public String abc() {
+            return "abc";
+        }
+
+        @GET
+        @Template
+        @Produces("*/*")
+        public String def() {
+            return "def";
+        }
+    }
+
+    @Path("explicitTemplateProducesClass")
+    @Produces("application/abc")
+    public static class ExplicitTemplateProducesClass extends ExplicitTemplateTest.ExplicitTemplate {
+    }
+
+    @Test
+    public void testProducesWildcard() throws Exception {
+        for (final String path : new String[] {"explicit-no-produces", "explicit-wildcard-produces",
+                "explicit-two-get-produces"}) {
+            final WebTarget target = target(path);
+
+            for (final String mediaType : new String[] {"application/def", "text/plain"}) {
+                final Properties p = new Properties();
+                p.load(target.request(mediaType).get(InputStream.class));
+
+                assertTrue(p.getProperty("path")
+                        .matches("/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/Explicit([a-zA-Z]+)"
+                                + "Template/index.def"));
+                assertEquals("def", p.getProperty("model"));
+                assertEquals("DefViewProcessor", p.getProperty("name"));
+            }
+        }
+    }
+
+    @Test
+    public void testProducesSpecific() throws Exception {
+        final WebTarget target = target("explicit-two-get-produces");
+
+        final Properties p = new Properties();
+        p.load(target.request("application/abc").get(InputStream.class));
+
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.abc",
+                p.getProperty("path"));
+        assertEquals("abc", p.getProperty("model"));
+        assertEquals("AbcViewProcessor", p.getProperty("name"));
+    }
+
+    @Test
+    public void testExplicitTemplateProducesClass() throws Exception {
+        final WebTarget target = target("explicitTemplateProducesClass");
+
+        Properties props = new Properties();
+        props.load(target.request().get(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/index.abc",
+                props.getProperty("path"));
+        assertEquals("method", props.getProperty("model"));
+
+        props = new Properties();
+        props.load(target.path("methodRelativePath").request().get(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/relative"
+                        + ".abc",
+                props.getProperty("path"));
+        assertEquals("methodRelativePath", props.getProperty("model"));
+
+        props = new Properties();
+        props.load(target.path("methodAbsolutePath").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.abc",
+                props.getProperty("path"));
+        assertEquals("methodAbsolutePath", props.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest.java
new file mode 100644
index 0000000..0bbd8b5
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ExplicitTemplateTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ExplicitTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    public static class CustomResolvingClass {
+    }
+
+    @Path("/")
+    public static class ExplicitTemplate {
+
+        @GET
+        @Template
+        public String method() {
+            return "method";
+        }
+
+        @GET
+        @Path("methodRelativePath")
+        @Template(name = "relative")
+        public String methodRelativePath() {
+            return "methodRelativePath";
+        }
+
+        @GET
+        @Path("methodAbsolutePath")
+        @Template(name = "/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute")
+        public String methodAbsolutePath() {
+            return "methodAbsolutePath";
+        }
+
+        @Path("subResource")
+        public ExplicitTemplate subResource() {
+            return new ExplicitTemplate();
+        }
+
+        @Path("subResourceTemplate")
+        @Template
+        public ExplicitTemplateSubResource subResourceTemplate() {
+            return new ExplicitTemplateSubResource();
+        }
+    }
+
+    public static class ExplicitTemplateSubResource {
+
+        @GET
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Test
+    public void testExplicitMethodTemplateSubResource() throws Exception {
+        assertEquals("get", target("subResourceTemplate").request().get(String.class));
+    }
+
+    @Test
+    public void testExplicitTemplate() throws Exception {
+        _testExplicitTemplate(target());
+    }
+
+    @Test
+    public void testExplicitTemplateSubResource() throws Exception {
+        _testExplicitTemplate(target("subResource"));
+    }
+
+    void _testExplicitTemplate(final WebTarget target) throws Exception {
+        Properties props = new Properties();
+        props.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/index.testp",
+                props.getProperty("path"));
+        assertEquals("method", props.getProperty("model"));
+
+        props = new Properties();
+        props.load(target.path("methodRelativePath").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/relative.testp",
+                props.getProperty("path"));
+        assertEquals("methodRelativePath", props.getProperty("model"));
+
+        props = new Properties();
+        props.load(target.path("methodAbsolutePath").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.testp",
+                props.getProperty("path"));
+        assertEquals("methodAbsolutePath", props.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.java
new file mode 100644
index 0000000..b3c0dce
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class FlatInheritedViewProcessorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ExplicitTemplate.class, ImplicitTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    public static class ExplicitTemplateBase {
+    }
+
+    @Path("/explicit")
+    public static class ExplicitTemplate extends ExplicitTemplateBase {
+
+        @GET
+        public Viewable get() {
+            return new Viewable("show", "get");
+        }
+
+        @Path("inherit")
+        @GET
+        public Viewable getInherited() {
+            return new Viewable("inherit", "get");
+        }
+
+        @Path("override")
+        @GET
+        public Viewable getOverriden() {
+            return new Viewable("override", "get");
+        }
+    }
+
+    @Test
+    public void testExplicitTemplate() throws IOException {
+        final WebTarget target = target("explicit");
+
+        Properties p = new Properties();
+        p.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("inherit").request().get(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.inherit.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("override").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplate.override.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+    }
+
+    public static class ImplicitTemplateBase {
+    }
+
+    @Path("/implicit")
+    @Template
+    public static class ImplicitTemplate extends ImplicitTemplateBase {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplate() throws IOException {
+        final WebTarget target = target("implicit");
+
+        Properties p = new Properties();
+        p.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("inherit").request().get(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.inherit.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("override").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplate.override.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.java
new file mode 100644
index 0000000..72a1f39
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class FlatViewProcessorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ExplicitTemplate.class, ImplicitTemplate.class, ImplicitExplicitTemplate.class,
+                ImplicitWithGetTemplate.class, ImplicitWithSubResourceGetTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    @Path("/explicit")
+    public static class ExplicitTemplate {
+
+        @GET
+        public Viewable get() {
+            return new Viewable("show", "get");
+        }
+
+        @POST
+        public Viewable post() {
+            return new Viewable("show", "post");
+        }
+    }
+
+    @Test
+    public void testExplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("explicit").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ExplicitTemplate.show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(request.post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE), InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ExplicitTemplate.show.testp",
+                p.getProperty("path"));
+        assertEquals("post", p.getProperty("model"));
+    }
+
+    @Path("/implicit")
+    @Template
+    public static class ImplicitTemplate {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("implicit").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitTemplate.index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+    }
+
+    @Path("/implicit-explicit")
+    @Template
+    public static class ImplicitExplicitTemplate {
+
+        public String toString() {
+            return "ImplicitExplicitTemplate";
+        }
+
+        @POST
+        public Viewable post() {
+            return new Viewable("show", "post");
+        }
+
+        @Path("sub")
+        @GET
+        public Viewable get() {
+            return new Viewable("show", "get");
+        }
+    }
+
+    @Test
+    public void testImplicitExplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("implicit-explicit").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitExplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(request.post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE), InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.show.testp",
+                p.getProperty("path"));
+        assertEquals("post", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target("implicit-explicit").path("sub").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+    }
+
+    @Path("/implicit-get")
+    @Template
+    public static class ImplicitWithGetTemplate {
+
+        @GET
+        @Produces("application/foo")
+        public String toString() {
+            return "ImplicitWithGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitWithGetTemplate() throws IOException {
+        final WebTarget target = target("implicit-get");
+
+        Properties p = new Properties();
+        p.load(target.request("text/plain").get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithGetTemplate.index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        assertEquals("ImplicitWithGetTemplate", target.request("application/foo").get(String.class));
+    }
+
+    @Path("/implicit-get-subresource")
+    @Template
+    public static class ImplicitWithSubResourceGetTemplate {
+
+        @Path("sub")
+        @GET
+        @Produces("application/foo")
+        public String toString() {
+            return "ImplicitWithSubResourceGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitWithSubResourceGetTemplate() throws IOException {
+        final WebTarget target = target("implicit-get-subresource").path("sub");
+
+        Properties p = new Properties();
+        p.load(target.request("text/plain").get(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithSubResourceGetTemplate.sub.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithSubResourceGetTemplate", p.getProperty("model"));
+
+        assertEquals("ImplicitWithSubResourceGetTemplate", target.request("application/foo").get(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest.java
new file mode 100644
index 0000000..bd705f0
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class ImplicitProducesViewProcessorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(
+                ImplicitTemplate.class,
+                ImplicitWithGetTemplate.class,
+                ImplicitWithSubResourceGetTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class).property("jersey.config.server.tracing", "ALL");
+    }
+
+    @Path("/implicit")
+    @Template
+    @Produces("text/plain;qs=.5")
+    public static class ImplicitTemplate {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplate() throws IOException {
+        final WebTarget target = target("implicit");
+
+        Properties p = new Properties();
+        Response cr = target.request("text/plain", "application/foo").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("application/foo", "text/plain").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("text/plain;q=0.5", "application/foo").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("application/foo", "text/plain;q=0.5").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+    }
+
+    @Path("/implicit-get")
+    @Template
+    @Produces("text/plain;qs=0.5")
+    public static class ImplicitWithGetTemplate {
+
+        @GET
+        @Produces("application/foo;qs=0.2")
+        public String toString() {
+            return "ImplicitWithGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitWithGetTemplate() throws IOException {
+        final WebTarget target = target("implicit-get");
+
+        Properties p = new Properties();
+        Response cr = target.request("text/plain", "application/foo").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index"
+                        + ".testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("application/foo", "text/plain").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index"
+                        + ".testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("text/plain", "application/foo;q=0.5").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index"
+                        + ".testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("application/foo;q=0.5", "text/plain").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index"
+                        + ".testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("*/*").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index"
+                        + ".testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        cr = target.request("text/plain;q=0.5", "application/foo").get(Response.class);
+        assertEquals(new MediaType("application", "foo"), cr.getMediaType());
+        assertEquals("ImplicitWithGetTemplate", cr.readEntity(String.class));
+    }
+
+    @Path("/implicit-get-subresource")
+    @Template
+    @Produces("text/plain;qs=0.5")
+    public static class ImplicitWithSubResourceGetTemplate {
+
+        @GET
+        @Path("sub")
+        @Produces("application/foo;qs=0.2")
+        public String toString() {
+            return "ImplicitWithSubResourceGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitWithSubResourceGetTemplate() throws IOException {
+        final WebTarget target = target("implicit-get-subresource").path("sub");
+
+        Properties p = new Properties();
+        Response cr = target.request("text/plain", "application/foo").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest"
+                        + "/ImplicitWithSubResourceGetTemplate/sub.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithSubResourceGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("application/foo", "text/plain").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest"
+                        + "/ImplicitWithSubResourceGetTemplate/sub.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithSubResourceGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("text/plain", "application/foo;q=0.5").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest"
+                        + "/ImplicitWithSubResourceGetTemplate/sub.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithSubResourceGetTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        cr = target.request("application/foo;q=0.5", "text/plain").get(Response.class);
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, cr.getMediaType());
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals(
+                "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest"
+                        + "/ImplicitWithSubResourceGetTemplate/sub.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithSubResourceGetTemplate", p.getProperty("model"));
+
+        assertEquals("ImplicitWithSubResourceGetTemplate", target.request("application/foo").get(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest.java
new file mode 100644
index 0000000..0de3486
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.Properties;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ImplicitTemplateProgrammaticTest extends JerseyTest {
+
+    @Template
+    public static class Handler {
+
+        private String constructor = "no-arg";
+
+        @SuppressWarnings("UnusedDeclaration")
+        public Handler() {
+        }
+
+        public Handler(final String constructor) {
+            this.constructor = constructor;
+        }
+
+        @Override
+        public String toString() {
+            return "Resource_" + constructor;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        Method toStringMethod = null;
+
+        try {
+            toStringMethod = Handler.class.getMethod("toString");
+        } catch (NoSuchMethodException e) {
+            // Eat that.
+        }
+
+        final Resource.Builder resourceBuilder = Resource.builder("implicit");
+
+        resourceBuilder.addMethod(HttpMethod.POST).consumes("application/foo").handledBy(Handler.class, toStringMethod);
+        resourceBuilder.addMethod(HttpMethod.POST).consumes("application/bar").handledBy(new Handler("arg"), toStringMethod);
+
+        return new ResourceConfig()
+                .registerResources(resourceBuilder.build())
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    @Test
+    public void testImplicitHandlerClass() throws Exception {
+        Properties p = new Properties();
+        p.load(target("implicit").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest/Handler/index.testp",
+                p.getProperty("path"));
+        assertEquals("Resource_no-arg", p.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest.java
new file mode 100644
index 0000000..7fb0ba8
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ImplicitTemplateTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(
+                ImplicitResource.class, AnotherImplicitResource.class,
+                ImplicitSingletonResource.class, ImplicitRootResource.class,
+                ImplicitGetResource.class, AnotherImplicitGetResource.class, AnotherAnotherImplicitGetResource.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    @Template
+    @Path("/implicit")
+    public static class ImplicitResource {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Path("/implicit")
+    public static class AnotherImplicitResource {
+
+        public String toString() {
+            return "ImplicitAnotherTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplateResources() throws Exception {
+        for (final String path : new String[] {"", "index", "get"}) {
+            WebTarget target = target("implicit");
+            String templateName = "index";
+
+            if (!"".equals(path)) {
+                templateName = path;
+                target = target.path(path);
+            }
+
+            Properties p = new Properties();
+            p.load(target.request().get(InputStream.class));
+            assertEquals(
+                    "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/" + templateName + ".testp",
+                    p.getProperty("path"));
+            assertEquals("ImplicitTemplate", p.getProperty("model"));
+        }
+    }
+
+    @Test
+    public void testImplicitTemplateResourcesNegative() throws Exception {
+        assertEquals(404, target("implicit").path("do-not-exist").request().get().getStatus());
+    }
+
+    @Path("/implicit-get")
+    @Produces("text/html")
+    public static class ImplicitGetResource {
+
+        @GET
+        public String get() {
+            return toString();
+        }
+
+        public String toString() {
+            return "ImplicitGetTemplate";
+        }
+    }
+
+    @Path("/implicit-get")
+    @Template
+    @Produces("text/plain")
+    public static class AnotherImplicitGetResource {
+
+        @GET
+        @Path("sub")
+        public String get() {
+            return toString();
+        }
+
+        public String toString() {
+            return "AnotherImplicitGetTemplate";
+        }
+    }
+
+    @Path("/implicit-get/another")
+    public static class AnotherAnotherImplicitGetResource {
+
+        @GET
+        public String get() {
+            return toString();
+        }
+
+        public String toString() {
+            return "AnotherAnotherImplicitGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitGetTemplateResources() throws Exception {
+        for (final String path : new String[] {"", "index", "get"}) {
+            WebTarget target = target("implicit-get");
+            String templateName = "index";
+
+            if (!"".equals(path)) {
+                templateName = path;
+                target = target.path(path);
+            }
+
+            Properties p = new Properties();
+            p.load(target.request("text/plain").get(InputStream.class));
+            assertEquals(
+                    "/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/" + templateName
+                            + ".testp",
+                    p.getProperty("path"));
+            assertEquals("AnotherImplicitGetTemplate", p.getProperty("model"));
+        }
+    }
+
+    @Template
+    @Singleton
+    @Path("/implicit-singleton")
+    public static class ImplicitSingletonResource {
+
+        private int counter = 0;
+
+        public String toString() {
+            return "ImplicitSingletonTemplate" + counter++;
+        }
+    }
+
+    @Test
+    public void testImplicitTemplateSingletonResources() throws Exception {
+        for (int i = 0; i < 10; i++) {
+            final WebTarget target = target("implicit-singleton");
+
+            Properties p = new Properties();
+            p.load(target.request().get(InputStream.class));
+            assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSingletonResource/index.testp",
+                    p.getProperty("path"));
+            assertEquals("ImplicitSingletonTemplate" + i, p.getProperty("model"));
+        }
+    }
+
+    @Path("/implicit-sub-resource")
+    public static class ImplicitRootResource {
+
+        @Path("sub")
+        public ImplicitSubResource getSubResource() {
+            return new ImplicitSubResource("ImplicitRootResource");
+        }
+    }
+
+    public static class ImplicitSubResource {
+
+        private final String string;
+
+        public ImplicitSubResource(final String string) {
+            this.string = string;
+        }
+
+        @Path("sub")
+        public ImplicitSubSubResource getSubResource() {
+            return new ImplicitSubSubResource(string + "ImplicitSubResource");
+        }
+    }
+
+    @Template
+    public static class ImplicitSubSubResource {
+
+        private final String string;
+
+        public ImplicitSubSubResource(final String string) {
+            this.string = string;
+        }
+
+        @Override
+        public String toString() {
+            return string + "ImplicitSubSubResource";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplateSubResources() throws Exception {
+        final WebTarget target = target("implicit-sub-resource").path("sub").path("sub");
+
+        Properties p = new Properties();
+        p.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSubSubResource/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitRootResourceImplicitSubResourceImplicitSubSubResource", p.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest.java
new file mode 100644
index 0000000..4d81576
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class ImplicitViewWithResourceFilterTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(ImplicitTemplate.class)
+                .register(MvcFeature.class)
+                .register(FilterOne.class)
+                .register(FilterTwo.class)
+                .register(TestViewProcessor.class);
+    }
+
+    @Path("/")
+    @Template
+    public static class ImplicitTemplate {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Priority(10)
+    public static class FilterOne implements ContainerRequestFilter, ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+            List<String> xTest = requestContext.getHeaders().get("X-TEST");
+            assertNull(xTest);
+
+            requestContext.getHeaders().add("X-TEST", "one");
+        }
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws
+                IOException {
+            List<String> rxTest = requestContext.getHeaders().get("X-TEST");
+            assertEquals(2, rxTest.size());
+            assertEquals("one", rxTest.get(0));
+            assertEquals("two", rxTest.get(1));
+
+            List<Object> xTest = responseContext.getHeaders().get("X-TEST");
+            assertEquals(1, xTest.size());
+            assertEquals("two", xTest.get(0));
+
+            assertNull(responseContext.getHeaders().get("Y-TEST"));
+            responseContext.getHeaders().add("Y-TEST", "one");
+        }
+    }
+
+    @Priority(20)
+    public static class FilterTwo implements ContainerRequestFilter, ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+            List<String> xTest = requestContext.getHeaders().get("X-TEST");
+            assertEquals(1, xTest.size());
+            assertEquals("one", xTest.get(0));
+
+            requestContext.getHeaders().add("X-TEST", "two");
+        }
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws
+                IOException {
+            List<String> rxTest = requestContext.getHeaders().get("X-TEST");
+            assertEquals(2, rxTest.size());
+            assertEquals("one", rxTest.get(0));
+            assertEquals("two", rxTest.get(1));
+
+            assertNull(responseContext.getHeaders().get("X-TEST"));
+            assertNull(responseContext.getHeaders().get("Y-TEST"));
+
+            responseContext.getHeaders().add("X-TEST", "two");
+        }
+    }
+
+    @Test
+    public void testImplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("/").request();
+
+        Response cr = request.get(Response.class);
+        assertEquals(200, cr.getStatus());
+        List<Object> xTest = cr.getMetadata().get("X-TEST");
+        assertEquals(1, xTest.size());
+        assertEquals("two", xTest.get(0));
+
+        List<Object> yTest = cr.getMetadata().get("Y-TEST");
+        assertEquals(1, yTest.size());
+        assertEquals("one", yTest.get(0));
+
+        Properties p = new Properties();
+        p.load(cr.readEntity(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest/ImplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest.java
new file mode 100644
index 0000000..88d847a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class InheritedViewProcessorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(
+                ExplicitTemplate.class,
+                ImplicitTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+    }
+
+    public static class ExplicitTemplateBase {
+    }
+
+    @Path("/explicit")
+    public static class ExplicitTemplate extends ExplicitTemplateBase {
+
+        @GET
+        public Viewable get() {
+            return new Viewable("show", "get");
+        }
+
+        @Path("inherit")
+        @GET
+        public Viewable getInherited() {
+            return new Viewable("inherit", "get");
+        }
+
+        @Path("override")
+        @GET
+        public Viewable getOverriden() {
+            return new Viewable("override", "get");
+        }
+    }
+
+    @Test
+    public void testExplicitTemplate() throws IOException {
+        final WebTarget target = target("explicit");
+
+        Properties p = new Properties();
+        p.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("inherit").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/inherit.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("override").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplate/override.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+    }
+
+    public static class ImplicitTemplateBase {
+    }
+
+    @Path("/implicit")
+    @Template
+    public static class ImplicitTemplate extends ImplicitTemplateBase {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplate() throws IOException {
+        final WebTarget target = target("implicit");
+
+        Properties p = new Properties();
+        p.load(target.request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("inherit").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/inherit.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target.path("override").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplate/override.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest.java
new file mode 100644
index 0000000..68ad121
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2014, 2018 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.mvc;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature;
+import org.glassfish.jersey.server.mvc.mustache.MustacheMvcFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * MVC encoding charset tests.
+ *
+ * @author Miroslav Fuksa
+ */
+@RunWith(Parameterized.class)
+public class MvcEncodingTest extends JerseyTest {
+
+    public static final String MESSAGE = "\\u0161\\u010d\\u0159\\u017e\\u00fd\\u00e1\\u00ed\\u00e9";
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {new FreemarkerMvcFeature(), "freemarker", "FreemarkerResource.ftl", "UTF-8"},
+                {new FreemarkerMvcFeature(), "freemarker", "FreemarkerResource.ftl", "UTF-16"},
+                {new FreemarkerMvcFeature(), "freemarker", "FreemarkerResource.ftl", "windows-1250"},
+                {new FreemarkerMvcFeature(), "freemarker", "FreemarkerResource.ftl", "ISO-8859-2"},
+                {new MustacheMvcFeature(), "mustache", "MustacheResource.mustache", "UTF-8"},
+                {new MustacheMvcFeature(), "mustache", "MustacheResource.mustache", "UTF-16"},
+                {new MustacheMvcFeature(), "mustache", "MustacheResource.mustache", "windows-1250"},
+                {new MustacheMvcFeature(), "mustache", "MustacheResource.mustache", "ISO-8859-2"},
+        });
+    }
+
+    private static String templateName;
+    private final String defaultEncoding;
+
+    public MvcEncodingTest(Feature feature, String propertySuffix, String templateName, String defaultEncoding) {
+        super(new ResourceConfig()
+                .register(feature)
+                .register(FreemarkerResource.class)
+                .property(MvcFeature.ENCODING + "." + propertySuffix, defaultEncoding));
+        MvcEncodingTest.templateName = templateName;
+        this.defaultEncoding = defaultEncoding;
+    }
+
+
+    @Path("resource")
+    public static class FreemarkerResource {
+        @GET
+        public Viewable get() {
+            final Map<String, String> map = new HashMap<String, String>();
+            map.put("user", MESSAGE);
+
+            return new Viewable("/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/" + templateName, map);
+        }
+
+        @GET
+        @Path("textplain")
+        @Produces("text/plain")
+        public Viewable getTextPlain() {
+            final Map<String, String> map = new HashMap<String, String>();
+            map.put("user", MESSAGE);
+
+            return new Viewable("/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/" + templateName, map);
+        }
+
+        @GET
+        @Path("textplainUTF16")
+        @Produces("text/plain;charset=UTF-16")
+        public Viewable getTextPlainUTF16() {
+            final Map<String, String> map = new HashMap<String, String>();
+            map.put("user", MESSAGE);
+
+            return new Viewable("/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/" + templateName, map);
+        }
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new GrizzlyTestContainerFactory();
+    }
+
+    @Test
+    public void testDefaultEncoding() {
+        final Response response = target().path("resource").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("Model:" + MESSAGE, response.readEntity(String.class));
+        Assert.assertEquals("*/*;charset=" + defaultEncoding, response.getMediaType().toString());
+        Assert.assertEquals(defaultEncoding, response.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER));
+    }
+
+    @Test
+    public void testTextPlainDefaultEncoding() {
+        final Response response = target().path("resource/textplain").request("*/*,text/plain,text/html").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("Model:" + MESSAGE, response.readEntity(String.class));
+        Assert.assertEquals("text/plain;charset=" + defaultEncoding, response.getMediaType().toString());
+        Assert.assertEquals(defaultEncoding, response.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER));
+    }
+
+    @Test
+    public void testTextPlain406() {
+        final Response response = target().path("resource/textplain").request("text/html").get();
+        Assert.assertEquals(406, response.getStatus());
+    }
+
+    @Test
+    public void testTextPlainUTF16() {
+        final Response response = target().path("resource/textplainUTF16").request("*/*,text/plain,text/html").get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("Model:" + MESSAGE, response.readEntity(String.class));
+        Assert.assertEquals("text/plain;charset=UTF-16", response.getMediaType().toString());
+        Assert.assertEquals("UTF-16", response.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest.java
new file mode 100644
index 0000000..39d3f77
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests that {@link Template} annotated methods are selected by the routing algorithms as if they
+ * would actually return {@link Viewable} instead of the model.
+ *
+ * @author Miroslav Fuksa
+ */
+public class TemplateMethodSelectionTest extends JerseyTest {
+
+    private static final Map<String, String> MODEL = new HashMap<String, String>() {{
+        put("a", "hello");
+        put("b", "world");
+    }};
+
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                TemplateAnnotatedResourceMethod.class,
+                TemplateAnnotatedResource.class,
+                BasicResource.class,
+                AsViewableResource.class,
+                NoTemplateResource.class,
+                LoggingFeature.class,
+                MvcFeature.class,
+                TestViewProcessor.class,
+                MoxyJsonFeature.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(MoxyJsonFeature.class);
+    }
+
+    public static MyBean getMyBean() {
+        final MyBean myBean = new MyBean();
+        myBean.setName("hello");
+        return myBean;
+    }
+
+    @XmlRootElement
+    public static class MyBean {
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(final String name) {
+            this.name = name;
+        }
+    }
+
+    @Path("annotatedMethod")
+    public static class TemplateAnnotatedResourceMethod {
+
+        @GET
+        @Produces(MediaType.TEXT_HTML)
+        @Template()
+        public Map<String, String> getAsHTML() {
+            return MODEL;
+        }
+
+        @GET
+        @Produces("application/json")
+        public MyBean getAsJSON() {
+            return getMyBean();
+        }
+    }
+
+    @Path("noTemplate")
+    public static class NoTemplateResource {
+
+        @GET
+        @Produces(MediaType.TEXT_HTML)
+        public Map<String, String> getAsHTML() {
+            return MODEL;
+        }
+
+        @GET
+        @Produces("application/json")
+        public MyBean getAsJSON() {
+            return getMyBean();
+        }
+    }
+
+    @Path("annotatedClass")
+    @Template
+    @Produces(MediaType.TEXT_HTML)
+    public static class TemplateAnnotatedResource {
+
+        @GET
+        @Produces("application/json")
+        public MyBean getAsJSON() {
+            return getMyBean();
+        }
+
+        @Override
+        public String toString() {
+            return "This toString() method will be used to get model.";
+        }
+    }
+
+    @Path("basic")
+    public static class BasicResource {
+        @GET
+        @Produces(MediaType.TEXT_HTML)
+        public String getAsHTML() {
+            return "Hello World";
+        }
+
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        public MyBean getAsJSON() {
+            return getMyBean();
+        }
+    }
+
+    @Path("viewable")
+    public static class AsViewableResource {
+        @GET
+        @Produces(MediaType.TEXT_HTML)
+        public Viewable getAsHTML() {
+            return new Viewable("index.testp", MODEL);
+        }
+
+        @GET
+        @Produces(MediaType.APPLICATION_JSON)
+        public MyBean getAsJSON() {
+            return getMyBean();
+        }
+    }
+
+    /**
+     * This test makes request for text/html which is preferred. The resource defines the method
+     * {@link org.glassfish.jersey.tests.e2e.server.mvc.TemplateMethodSelectionTest.TemplateAnnotatedResourceMethod#getAsHTML()}
+     * which returns {@link Map} for which there is not {@link javax.ws.rs.ext.MessageBodyWriter}. The absence of the
+     * writer would cause that the method would not have been selected but as the {@link Template} annotation
+     * is on the method, the {@link org.glassfish.jersey.server.internal.routing.MethodSelectingRouter} considers
+     * it as if this would have been {@link Viewable} instead of the {@link Map}.
+     */
+    @Test
+    public void testAnnotatedMethodByTemplateHtml() {
+        final Response response = target().path("annotatedMethod").request("text/html;q=0.8", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_HTML_TYPE, response.getMediaType());
+        assertThat(response.readEntity(String.class),
+                anyOf(containsString("{b=world, a=hello}"), containsString("{a=hello, b=world}")));
+    }
+
+    @Test
+    public void testAnnotatedMethodByTemplateJson() {
+        final Response response = target().path("annotatedMethod").request("text/html;q=0.6", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+        assertEquals("hello", response.readEntity(MyBean.class).getName());
+    }
+
+    @Test
+    public void testAnnotatedClassByTemplateHtml() {
+        final Response response = target().path("annotatedClass").request("text/html;q=0.8", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_HTML_TYPE, response.getMediaType());
+        assertTrue(response.readEntity(String.class).contains("model=This toString() method will be used to get model."));
+    }
+
+    @Test
+    public void testAnnotatedClassByTemplateJson() {
+        final Response response = target().path("annotatedClass").request("text/html;q=0.6", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+        assertEquals("hello", response.readEntity(MyBean.class).getName());
+    }
+
+    @Test
+    public void testBasicHtml() {
+        final Response response = target().path("basic").request("text/html;q=0.8", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_HTML_TYPE, response.getMediaType());
+        assertTrue(response.readEntity(String.class).contains("Hello World"));
+    }
+
+    @Test
+    public void testBasicJson() {
+        final Response response = target().path("basic").request("text/html;q=0.6", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+        assertEquals("hello", response.readEntity(MyBean.class).getName());
+    }
+
+    @Test
+    public void testAsViewableHtml() {
+        final Response response = target().path("viewable").request("text/html;q=0.8", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_HTML_TYPE, response.getMediaType());
+        assertThat(response.readEntity(String.class),
+                anyOf(containsString("{b=world, a=hello}"), containsString("{a=hello, b=world}")));
+    }
+
+    @Test
+    public void testAsViewableJson() {
+        final Response response = target().path("viewable").request("text/html;q=0.6", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+        assertEquals("hello", response.readEntity(MyBean.class).getName());
+    }
+
+    /**
+     * This test verifies that there is really no {@link javax.ws.rs.ext.MessageBodyWriter}
+     * for {@code Map<String,String>}}. text/html is requested but application/json is chosen there is no
+     * MBW for {@code Map}.
+     */
+    @Test
+    public void testNoTemplateHtml() {
+        final Response response = target().path("noTemplate").request("text/html;q=0.9", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+        assertEquals("hello", response.readEntity(MyBean.class).getName());
+    }
+
+    @Test
+    public void testNoTemplateJson() {
+        final Response response = target().path("noTemplate").request("text/html;q=0.6", "application/json;q=0.7").get();
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+        assertEquals("hello", response.readEntity(MyBean.class).getName());
+    }
+
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest.java
new file mode 100644
index 0000000..15be3f6
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.e2e.server.mvc.provider.TestViewProcessor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class ViewProcessorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        ResourceConfig register = new ResourceConfig(
+                ExplicitTemplate.class,
+                ImplicitTemplate.class,
+                ImplicitExplicitTemplate.class,
+                ImplicitWithGetTemplate.class,
+                ImplicitWithSubResourceGetTemplate.class)
+                .register(MvcFeature.class)
+                .register(TestViewProcessor.class);
+        register.property(ServerProperties.WADL_FEATURE_DISABLE, true);
+        return register;
+    }
+
+    @Template
+    @Path("/explicit")
+    public static class ExplicitTemplate {
+
+        @GET
+        @Template
+        public Response get() {
+            return Response.ok().entity(new Viewable("show", "get")).build();
+        }
+
+        @POST
+        public Viewable post() {
+            return new Viewable("show", "post");
+        }
+
+        @GET
+        @Path("absolute")
+        public Viewable getAbs() {
+            return new Viewable("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show",
+                    "get");
+        }
+
+        @POST
+        @Path("absolute")
+        public Viewable postAbs() {
+            return new Viewable("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show",
+                    "post");
+        }
+    }
+
+    @Test
+    public void testExplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("explicit").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(request.post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE), InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/show.testp",
+                p.getProperty("path"));
+        assertEquals("post", p.getProperty("model"));
+    }
+
+    @Test
+    public void testExplicitAbsoluteTemplate() throws IOException {
+        final Invocation.Builder request = target("explicit").path("absolute").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(request.post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE), InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show.testp",
+                p.getProperty("path"));
+        assertEquals("post", p.getProperty("model"));
+    }
+
+    @Path("/implicit")
+    @Template
+    public static class ImplicitTemplate {
+
+        public String toString() {
+            return "ImplicitTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("implicit").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitTemplate", p.getProperty("model"));
+    }
+
+    @Path("/implicit-explicit")
+    @Template
+    public static class ImplicitExplicitTemplate {
+
+        public String toString() {
+            return "ImplicitExplicitTemplate";
+        }
+
+        @POST
+        public Viewable post() {
+            return new Viewable("show", "post");
+        }
+
+        @GET
+        @Path("sub")
+        public Viewable get() {
+            return new Viewable("show", "get");
+        }
+    }
+
+    @Test
+    public void testImplicitExplicitTemplate() throws IOException {
+        final Invocation.Builder request = target("implicit-explicit").request();
+
+        Properties p = new Properties();
+        p.load(request.get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitExplicitTemplate", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(request.post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE), InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/show.testp",
+                p.getProperty("path"));
+        assertEquals("post", p.getProperty("model"));
+
+        p = new Properties();
+        p.load(target("implicit-explicit").path("sub").request().get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/show.testp",
+                p.getProperty("path"));
+        assertEquals("get", p.getProperty("model"));
+    }
+
+    @Path("/implicit-get")
+    @Template
+    public static class ImplicitWithGetTemplate {
+
+        @GET
+        @Produces("application/foo")
+        public String toString() {
+            return "ImplicitWithGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitWithGetTemplate() throws IOException {
+        final WebTarget target = target("implicit-get");
+
+        Properties p = new Properties();
+        p.load(target.request("text/plain").get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithGetTemplate/index.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithGetTemplate", p.getProperty("model"));
+
+        assertEquals("ImplicitWithGetTemplate", target.request("application/foo").get(String.class));
+    }
+
+    @Path("/implicit-get-subresource")
+    @Template
+    public static class ImplicitWithSubResourceGetTemplate {
+
+        @GET
+        @Path("sub")
+        @Produces("application/foo")
+        public String toString() {
+            return "ImplicitWithSubResourceGetTemplate";
+        }
+    }
+
+    @Test
+    public void testImplicitWithSubResourceGetTemplate() throws IOException {
+        final WebTarget target = target("implicit-get-subresource").path("sub");
+
+        Properties p = new Properties();
+        p.load(target.request("text/plain").get(InputStream.class));
+        assertEquals("/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp",
+                p.getProperty("path"));
+        assertEquals("ImplicitWithSubResourceGetTemplate", p.getProperty("model"));
+
+        assertEquals("ImplicitWithSubResourceGetTemplate", target.request("application/foo").get(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/AbcViewProcessor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/AbcViewProcessor.java
new file mode 100644
index 0000000..07772c6
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/AbcViewProcessor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc.provider;
+
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Michal Gajdos
+ */
+public class AbcViewProcessor extends TestViewProcessor {
+
+    @Override
+    protected String getExtension() {
+        return ".abc";
+    }
+
+    @Override
+    protected String getViewProcessorName() {
+        return "AbcViewProcessor";
+    }
+
+    @Override
+    protected boolean acceptMediaType(final MediaType mediaType) {
+        return new MediaType("application", "abc").equals(mediaType);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/CustomViewableContext.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/CustomViewableContext.java
new file mode 100644
index 0000000..edbd8f3
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/CustomViewableContext.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc.provider;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.server.mvc.internal.TemplateHelper;
+import org.glassfish.jersey.server.mvc.spi.ResolvedViewable;
+import org.glassfish.jersey.server.mvc.spi.TemplateProcessor;
+import org.glassfish.jersey.server.mvc.spi.ViewableContext;
+
+/**
+ * Custom {@link ViewableContext viewable context}.
+ *
+ * @author Michal Gajdos
+ */
+@Provider
+public class CustomViewableContext implements ViewableContext {
+
+    @Override
+    public ResolvedViewable resolveViewable(final Viewable viewable, final MediaType mediaType,
+                                            final Class<?> resourceClass, final TemplateProcessor templateProcessor) {
+        final String path = TemplateHelper.getTemplateName(viewable);
+        final Object templateReference = templateProcessor.resolve("/CustomViewableContext/" + path,
+                MediaType.TEXT_PLAIN_TYPE);
+
+        if (templateReference != null) {
+            return new ResolvedViewable(templateProcessor, templateReference, viewable, MediaType.TEXT_PLAIN_TYPE);
+        }
+
+        return null;
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/DefViewProcessor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/DefViewProcessor.java
new file mode 100644
index 0000000..06302db
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/DefViewProcessor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc.provider;
+
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Michal Gajdos
+ */
+public class DefViewProcessor extends TestViewProcessor {
+
+    @Override
+    protected String getExtension() {
+        return ".def";
+    }
+
+    @Override
+    protected String getViewProcessorName() {
+        return "DefViewProcessor";
+    }
+
+    @Override
+    protected boolean acceptMediaType(final MediaType mediaType) {
+        return !new MediaType("application", "abc").equals(mediaType);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/TestViewProcessor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/TestViewProcessor.java
new file mode 100644
index 0000000..621256b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/TestViewProcessor.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc.provider;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.util.Collection;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.server.mvc.spi.TemplateProcessor;
+import org.glassfish.jersey.server.validation.ValidationError;
+
+/**
+ * @author Paul Sandoz
+ * @author Michal Gajdos
+ */
+public class TestViewProcessor implements TemplateProcessor<String> {
+
+    @Override
+    public String resolve(String path, final MediaType mediaType) {
+        final String extension = getExtension();
+
+        if (!path.endsWith(extension)) {
+            path = path + extension;
+        }
+
+        final URL u = this.getClass().getResource(path);
+        if (u == null || !acceptMediaType(mediaType)) {
+            return null;
+        }
+        return path;
+    }
+
+    protected boolean acceptMediaType(final MediaType mediaType) {
+        return true;
+    }
+
+    @Override
+    public void writeTo(String templateReference, Viewable viewable, MediaType mediaType,
+                        MultivaluedMap<String, Object> httpHeaders, OutputStream out) throws IOException {
+
+        final PrintStream ps = new PrintStream(out);
+        ps.print("name=");
+        ps.print(getViewProcessorName());
+        ps.println();
+        ps.print("path=");
+        ps.print(templateReference);
+        ps.println();
+        ps.print("model=");
+        ps.print(getModel(viewable.getModel()));
+        ps.println();
+    }
+
+    private String getModel(final Object model) {
+        if (model instanceof Collection) {
+            StringBuilder builder = new StringBuilder();
+            for (final Object object : (Collection) model) {
+                builder.append(getModel(object)).append(',');
+            }
+            return builder.delete(builder.length() - 1, builder.length()).toString();
+        } else if (model instanceof ValidationError) {
+            final ValidationError error = (ValidationError) model;
+            return error.getMessageTemplate() + "_" + error.getPath() + "_" + error.getInvalidValue();
+        }
+        return model.toString();
+    }
+
+    protected String getExtension() {
+        return ".testp";
+    }
+
+    protected String getViewProcessorName() {
+        return "TestViewProcessor";
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest.java
new file mode 100644
index 0000000..719e1a5
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2013, 2018 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.mvc.spi;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+import javax.servlet.ServletContext;
+
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.MvcFeature;
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+import org.glassfish.jersey.server.mvc.spi.AbstractTemplateProcessor;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.spi.TestContainerException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        AbstractTemplateProcessorTest.FactoryInstanceTest.class,
+        AbstractTemplateProcessorTest.FactoryInstanceNegativeTest.class,
+        AbstractTemplateProcessorTest.FactoryClassTest.class,
+        AbstractTemplateProcessorTest.FactoryClassNegativeTest.class,
+        AbstractTemplateProcessorTest.FactoryClassNameTest.class,
+        AbstractTemplateProcessorTest.FactoryClassNameNegativeTest.class,
+        AbstractTemplateProcessorTest.CachePositiveTest.class,
+        AbstractTemplateProcessorTest.CachePositiveStringTest.class,
+        AbstractTemplateProcessorTest.CacheNegativeTest.class,
+        AbstractTemplateProcessorTest.CacheInvalidTest.class
+})
+public class AbstractTemplateProcessorTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        @Template
+        public String get() {
+            return "ko";
+        }
+    }
+
+    public static class TestFactory {
+
+        private final String value;
+
+        public TestFactory() {
+            this("Injected Test Factory");
+        }
+
+        public TestFactory(final String value) {
+            this.value = value;
+        }
+    }
+
+    public static class FactoryTemplateProcessor extends AbstractTemplateProcessor<String> {
+
+        private final TestFactory factory;
+
+        @Inject
+        public FactoryTemplateProcessor(Configuration config, InjectionManager injectionManager) {
+            super(config, injectionManager.getInstance(ServletContext.class), "factory", "fct");
+
+            this.factory = getTemplateObjectFactory(injectionManager::createAndInitialize, TestFactory.class, Values.lazy(
+                    (Value<TestFactory>) () -> new TestFactory("Default Test Factory")));
+        }
+
+        @Override
+        protected String resolve(final String templatePath, final Reader reader) throws Exception {
+            return factory.value;
+        }
+
+        @Override
+        public void writeTo(final String templateReference, final Viewable viewable, final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders, final OutputStream out)
+                throws IOException {
+            out.write(templateReference.getBytes());
+        }
+    }
+
+    public abstract static class FactoryTest extends JerseyTest {
+
+        private final String expectedMessage;
+
+        FactoryTest(final Object factory, final String expectedMessage) throws TestContainerException {
+            super(new ResourceConfig(Resource.class)
+                    .register(MvcFeature.class)
+                    .register(FactoryTemplateProcessor.class)
+                    .property(MvcFeature.TEMPLATE_OBJECT_FACTORY + ".factory", factory));
+
+            this.expectedMessage = expectedMessage;
+        }
+
+        @Test
+        public void testFactory() throws Exception {
+            final Response response = target().request().get();
+
+            assertThat(response.getStatus(), is(200));
+            assertThat(response.readEntity(String.class), is(expectedMessage));
+        }
+    }
+
+    public static class FactoryInstanceTest extends FactoryTest {
+
+        public FactoryInstanceTest() throws TestContainerException {
+            super(new TestFactory(), "Injected Test Factory");
+        }
+    }
+
+    public static class FactoryInstanceNegativeTest extends FactoryTest {
+
+        public FactoryInstanceNegativeTest() throws TestContainerException {
+            super("Default Test Factory", "Default Test Factory");
+        }
+    }
+
+    public static class FactoryClassTest extends FactoryTest {
+
+        public FactoryClassTest() throws TestContainerException {
+            super(TestFactory.class, "Injected Test Factory");
+        }
+    }
+
+    public static class FactoryClassNegativeTest extends FactoryTest {
+
+        public FactoryClassNegativeTest() throws TestContainerException {
+            super(String.class, "Default Test Factory");
+        }
+    }
+
+    public static class FactoryClassNameTest extends FactoryTest {
+
+        public FactoryClassNameTest() throws TestContainerException {
+            super(TestFactory.class.getName(), "Injected Test Factory");
+        }
+    }
+
+    public static class FactoryClassNameNegativeTest extends FactoryTest {
+
+        public FactoryClassNameNegativeTest() throws TestContainerException {
+            super(String.class.getName(), "Default Test Factory");
+        }
+    }
+
+    public static class CacheTemplateProcessor extends AbstractTemplateProcessor<String> {
+
+        private int i = 0;
+
+        @Inject
+        public CacheTemplateProcessor(Configuration config, InjectionManager injectionManager) {
+            super(config, injectionManager.getInstance(ServletContext.class), "factory", "fct");
+        }
+
+        @Override
+        protected String resolve(final String templatePath, final Reader reader) throws Exception {
+            return "" + i++;
+        }
+
+        @Override
+        public void writeTo(final String templateReference, final Viewable viewable, final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream out) throws IOException {
+            out.write(templateReference.getBytes());
+        }
+    }
+
+    public abstract static class CacheTest extends JerseyTest {
+
+        private final boolean cache;
+
+        CacheTest(final Object cache) throws TestContainerException {
+            super(new ResourceConfig(Resource.class)
+                    .register(MvcFeature.class)
+                    .register(CacheTemplateProcessor.class)
+                    .property(MvcFeature.CACHE_TEMPLATES + ".factory", cache));
+
+            this.cache = Boolean.valueOf(cache.toString());
+        }
+
+        @Test
+        public void testCache() throws Exception {
+            Response response = target().request().get();
+
+            assertThat(response.getStatus(), is(200));
+            assertThat(response.readEntity(String.class), is("0"));
+
+            response = target().request().get();
+
+            assertThat(response.getStatus(), is(200));
+            assertThat(response.readEntity(String.class), is(cache ? "0" : "1"));
+        }
+    }
+
+    public static class CachePositiveTest extends CacheTest {
+
+        public CachePositiveTest() throws TestContainerException {
+            super(true);
+        }
+    }
+
+    public static class CachePositiveStringTest extends CacheTest {
+
+        public CachePositiveStringTest() throws TestContainerException {
+            super("true");
+        }
+    }
+
+    public static class CacheNegativeTest extends CacheTest {
+
+        public CacheNegativeTest() throws TestContainerException {
+            super(false);
+        }
+    }
+
+    public static class CacheInvalidTest extends CacheTest {
+
+        public CacheInvalidTest() throws TestContainerException {
+            super("invalid");
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/package-info.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/package-info.java
new file mode 100644
index 0000000..c0fb065
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012, 2018 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
+ */
+
+/**
+ * Jersey End to End server test classes.
+ */
+package org.glassfish.jersey.tests.e2e.server;
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/InheritanceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/InheritanceTest.java
new file mode 100644
index 0000000..5472975
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/InheritanceTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013, 2018 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.routing;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class InheritanceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class).register(WriterA.class).register(WriterB.class).register(WriterString.class);
+    }
+
+    public static class A {
+
+        String value;
+
+        public A() {
+            this.value = "a";
+        }
+
+        public A(final String value) {
+            this.value = value;
+        }
+    }
+
+    public static class B extends A {
+
+        public B() {
+            super("b");
+        }
+
+        public B(final String value) {
+            super("b" + value);
+        }
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public A getA() {
+            return new B("a");
+        }
+    }
+
+    public static class WriterA implements MessageBodyWriter<A> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return A.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(final A a, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final A a, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(("a" + a.value).getBytes());
+        }
+    }
+
+    @Produces({"text/plain"})
+    public static class WriterString implements MessageBodyWriter<String> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(s.getBytes());
+        }
+    }
+
+    @Produces("b/b")
+    public static class WriterB implements MessageBodyWriter<B> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return B.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(final B b, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final B b,
+                            final Class<?> type,
+                            final Type genericType,
+                            final Annotation[] annotations,
+                            final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(("b" + b.value).getBytes());
+        }
+    }
+
+    @Test
+    public void testWriterEntityInheritance() throws Exception {
+        assertThat(target().request("a/b").get(String.class), equalTo("aba"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/ResponseMediaTypeFromProvidersTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/ResponseMediaTypeFromProvidersTest.java
new file mode 100644
index 0000000..09ed78d
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/ResponseMediaTypeFromProvidersTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2013, 2018 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.routing;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ResponseMediaTypeFromProvidersTest extends JerseyTest {
+
+    @Path("response")
+    public static class ResponseResource {
+
+        private List<String> getList() {
+            // must be an ArrayList. Arrays.asList creates Arrays$ArrayList.
+            return Arrays.asList("array", "list").stream().collect(Collectors.toCollection(ArrayList::new));
+        }
+
+        @GET
+        @Path("list")
+        public Response responseList() {
+            return Response.ok(getList()).build();
+        }
+    }
+
+    @Provider
+    public static class CollectionMessageBodyWriter implements MessageBodyWriter<Collection<?>> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return type.equals(ArrayList.class) && genericType.equals(ArrayList.class);
+        }
+
+        @Override
+        public long getSize(final Collection<?> objects, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final Collection<?> objects,
+                            final Class<?> type,
+                            final Type genericType,
+                            final Annotation[] annotations,
+                            final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write("OK".getBytes());
+        }
+    }
+
+    @Provider
+    public static class IncorrectCollectionMessageBodyWriter implements MessageBodyWriter<Collection<?>> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return !new CollectionMessageBodyWriter().isWriteable(type, genericType, annotations, mediaType);
+        }
+
+        @Override
+        public long getSize(final Collection<?> objects, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final Collection<?> objects,
+                            final Class<?> type,
+                            final Type genericType,
+                            final Annotation[] annotations,
+                            final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write("ERROR".getBytes());
+        }
+    }
+
+    @Test
+    public void testResponseList() throws Exception {
+        final Response response = target("response").path("list").request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("OK"));
+    }
+
+    public static class StringBean {
+
+        private String value;
+
+        public StringBean() {
+        }
+
+        public StringBean(final String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(final String value) {
+            this.value = value;
+        }
+    }
+
+    @Provider
+    public static class StringBeanMessageBodyWriter implements MessageBodyWriter<StringBean> {
+
+        public static MediaType STRING_BEAN_MT = new MediaType("string", "bean");
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return type.equals(StringBean.class) && STRING_BEAN_MT.equals(mediaType);
+        }
+
+        @Override
+        public long getSize(final StringBean objects, final Class<?> type, final Type genericType,
+                            final Annotation[] annotations, final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final StringBean objects,
+                            final Class<?> type,
+                            final Type genericType,
+                            final Annotation[] annotations,
+                            final MediaType mediaType,
+                            final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(objects.getValue().getBytes());
+        }
+    }
+
+    @Path("AcceptableNonWriteableMethodResource")
+    public static class AcceptableNonWriteableMethodResource {
+
+        @GET
+        public StringBean getStringBean() {
+            return new StringBean("getStringBean");
+        }
+
+        @GET
+        @Produces("text/html")
+        public StringBean getTextHtml() {
+            return new StringBean("getTextHtml");
+        }
+
+        @GET
+        @Produces("text/xhtml")
+        public StringBean getTextXHtml() {
+            return new StringBean("getTextXHtml");
+        }
+
+        @POST
+        @Consumes("string/bean")
+        @Produces("string/bean")
+        public StringBean postStringBean(final StringBean stringBean) {
+            return stringBean;
+        }
+
+        @POST
+        @Produces("string/bean")
+        public StringBean postStringBean(final String string) {
+            return new StringBean("postStringBean_" + string);
+        }
+
+        @POST
+        @Consumes("string/bean")
+        @Path("response")
+        public Response postResponse(final StringBean stringBean) {
+            return Response.ok(stringBean).type("string/bean").build();
+        }
+
+        @POST
+        @Path("response")
+        public Response postResponse(final String string) {
+            return Response.ok(new StringBean("postStringBean_" + string)).type("string/bean").build();
+        }
+    }
+
+    @Test
+    public void testGetMethodRouting() throws Exception {
+        final Response response = target("AcceptableNonWriteableMethodResource").request("text/html", "text/xhtml",
+                "string/bean;q=0.2").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.getMediaType(), equalTo(StringBeanMessageBodyWriter.STRING_BEAN_MT));
+        assertThat(response.readEntity(String.class), equalTo("getStringBean"));
+    }
+
+    @Test
+    public void testPostMethodRouting() throws Exception {
+        final Response response = target("AcceptableNonWriteableMethodResource").request("text/html", "text/xhtml",
+                "string/bean;q=0.2").post(Entity.entity("value", "string/bean"));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.getMediaType(), equalTo(StringBeanMessageBodyWriter.STRING_BEAN_MT));
+        assertThat(response.readEntity(String.class), equalTo("postStringBean_value"));
+    }
+
+    @Test
+    public void testPostMethodRoutingWildcard() throws Exception {
+        final Response response = target("AcceptableNonWriteableMethodResource").request("*/*")
+                .post(Entity.entity("value", "string/bean"));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.getMediaType(), equalTo(StringBeanMessageBodyWriter.STRING_BEAN_MT));
+        assertThat(response.readEntity(String.class), equalTo("postStringBean_value"));
+    }
+
+    @Test
+    public void testPostMethodRoutingResponse() throws Exception {
+        final Response response = target("AcceptableNonWriteableMethodResource").path("response").request()
+                .post(Entity.entity("value", "string/bean"));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.getMediaType(), equalTo(StringBeanMessageBodyWriter.STRING_BEAN_MT));
+        assertThat(response.readEntity(String.class), equalTo("postStringBean_value"));
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ResponseResource.class, AcceptableNonWriteableMethodResource.class)
+                .register(CollectionMessageBodyWriter.class)
+                .register(IncorrectCollectionMessageBodyWriter.class)
+                .register(StringBeanMessageBodyWriter.class);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/CustomFeature.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/CustomFeature.java
new file mode 100644
index 0000000..3a37268
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/CustomFeature.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext1WriterInterceptor;
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext2WriterInterceptor;
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext3WriterInterceptor;
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext4WriterInterceptor;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class CustomFeature implements Feature {
+
+    @Override
+    public boolean configure(final FeatureContext context) {
+        context.register(Ext3WriterInterceptor.class, 1000);
+        context.register(Ext2WriterInterceptor.class, 100);
+        context.register(Ext1WriterInterceptor.INSTANCE, 500);
+        context.register(Ext4WriterInterceptor.INSTANCE, 1);
+        return true;
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/RankedProviderScanningTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/RankedProviderScanningTest.java
new file mode 100644
index 0000000..9b5defa
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/RankedProviderScanningTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class RankedProviderScanningTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig().packages(false, RankedProviderScanningTest.class.getPackage().getName());
+    }
+
+    @Test
+    public void testRankedProviderScanning() throws Exception {
+        WebTarget t = target();
+        t.register(LoggingFeature.class);
+
+        Response r = t.path("/").request().get();
+
+        assertEquals(200, r.getStatus());
+        assertEquals("get-ext4-ext2-ext1-ext3", r.readEntity(String.class));
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/Resource.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/Resource.java
new file mode 100644
index 0000000..916ec20
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/Resource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Configuration;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext1WriterInterceptor;
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext2WriterInterceptor;
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext3WriterInterceptor;
+import org.glassfish.jersey.tests.e2e.server.scanning.ext.Ext4WriterInterceptor;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+* @author Michal Gajdos
+*/
+@Path("/")
+public class Resource {
+
+    private final Configuration config;
+
+    @Inject
+    public Resource(final Configuration config) {
+        this.config = config;
+    }
+
+    @GET
+    public String get() {
+        assertTrue(config.getClasses().size() >= 4); // e.g. WADL resource can be there too.
+        assertTrue(config.isRegistered(CustomFeature.class));
+        assertTrue(config.isRegistered(Ext2WriterInterceptor.class));
+        assertTrue(config.isRegistered(Ext3WriterInterceptor.class));
+        assertTrue(config.isRegistered(Resource.class));
+        assertTrue(config.getClasses().contains(CustomFeature.class));
+        assertTrue(config.getClasses().contains(Ext2WriterInterceptor.class));
+        assertTrue(config.getClasses().contains(Ext3WriterInterceptor.class));
+        assertTrue(config.getClasses().contains(Resource.class));
+
+        assertTrue(config.getInstances().size() >= 2);
+        assertTrue(config.getInstances().contains(Ext1WriterInterceptor.INSTANCE));
+        assertTrue(config.getInstances().contains(Ext4WriterInterceptor.INSTANCE));
+        assertTrue(config.isRegistered(Ext1WriterInterceptor.class));
+        assertTrue(config.isRegistered(Ext4WriterInterceptor.class));
+
+        assertTrue(config.isEnabled(CustomFeature.class));
+
+        return "get";
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext1WriterInterceptor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext1WriterInterceptor.java
new file mode 100644
index 0000000..c7ab2ea
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext1WriterInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext1WriterInterceptor implements WriterInterceptor {
+
+    public static final Ext1WriterInterceptor INSTANCE = new Ext1WriterInterceptor();
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext1");
+        context.proceed();
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext2WriterInterceptor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext2WriterInterceptor.java
new file mode 100644
index 0000000..2da3a23
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext2WriterInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext2WriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext2");
+        context.proceed();
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext3WriterInterceptor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext3WriterInterceptor.java
new file mode 100644
index 0000000..b01e46e
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext3WriterInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext3WriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext3");
+        context.proceed();
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext4WriterInterceptor.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext4WriterInterceptor.java
new file mode 100644
index 0000000..b1aca63
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext4WriterInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 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.scanning.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext4WriterInterceptor implements WriterInterceptor {
+
+    public static final Ext4WriterInterceptor INSTANCE = new Ext4WriterInterceptor();
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext4");
+        context.proceed();
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicBadSubResource.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicBadSubResource.java
new file mode 100644
index 0000000..aaa9b86
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicBadSubResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+
+import javax.validation.constraints.Null;
+
+/**
+ * @author Michal Gajdos
+ */
+public class BasicBadSubResource {
+
+    @Context
+    @Null
+    private ResourceContext resourceContext;
+
+    @GET
+    public String get() {
+        throw new RuntimeException("Should not get here.");
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicResource.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicResource.java
new file mode 100644
index 0000000..90c5938
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicResource.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Null;
+import javax.validation.constraints.Size;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("beanvalidation")
+public class BasicResource {
+
+    @NotNull
+    @Context
+    private ResourceContext resourceContext;
+
+    private String getInputParamsResponse(final String path,
+                                          final String matrix,
+                                          final String query,
+                                          final String header,
+                                          final String cookie,
+                                          final String form) {
+
+        return path + '_' + matrix + '_' + query + '_' + header + '_' + cookie + '_' + form;
+    }
+
+    @POST
+    @Path("basicParam/{path: .*}")
+    @Consumes("application/x-www-form-urlencoded")
+    @SuppressWarnings("UnusedParameters")
+    public String validateParametersBasicConstraint(
+            @NotNull @PathParam("path") final String path,
+            @NotNull @MatrixParam("matrix") final String matrix,
+            @NotNull @QueryParam("query") final String query,
+            @NotNull @HeaderParam("header") final String header,
+            @NotNull @CookieParam("cookie") final String cookie,
+            @NotNull @FormParam("form") final String form,
+            @NotNull @Context final Request request) {
+
+        return getInputParamsResponse(path, matrix, query, header, cookie, form);
+    }
+
+    @POST
+    @Path("basicDefaultParam/{path: .*}")
+    @Consumes("application/x-www-form-urlencoded")
+    @SuppressWarnings("UnusedParameters")
+    public String validateParametersDefaultBasicConstraint(
+            @NotNull @DefaultValue("pathParam") @PathParam("path") final String path,
+            @NotNull @DefaultValue("matrixParam") @MatrixParam("matrix") final String matrix,
+            @NotNull @DefaultValue("queryParam") @QueryParam("query") final String query,
+            @NotNull @DefaultValue("headerParam") @HeaderParam("header") final String header,
+            @NotNull @DefaultValue("cookieParam") @CookieParam("cookie") final String cookie,
+            @NotNull @DefaultValue("formParam") @FormParam("form") final String form,
+            @NotNull @Context final Request request) {
+
+        return getInputParamsResponse(path, matrix, query, header, cookie, form);
+    }
+
+    @POST
+    @Path("customParam/{path: .*}")
+    @Consumes("application/x-www-form-urlencoded")
+    public String validateParametersCustomConstraint(
+            @ParamConstraint @PathParam("path") final String path,
+            @ParamConstraint @MatrixParam("matrix") final String matrix,
+            @ParamConstraint @QueryParam("query") final String query,
+            @ParamConstraint @HeaderParam("header") final String header,
+            @ParamConstraint @CookieParam("cookie") final String cookie,
+            @ParamConstraint @FormParam("form") final String form) {
+
+        return getInputParamsResponse(path, matrix, query, header, cookie, form);
+    }
+
+    @POST
+    @Path("mixedParam/{path: .*}")
+    @Consumes("application/x-www-form-urlencoded")
+    public String validateParametersMixedConstraint(
+            @Size(max = 11) @ParamConstraint @PathParam("path") final String path,
+            @Size(max = 11) @ParamConstraint @MatrixParam("matrix") final String matrix,
+            @Size(max = 11) @ParamConstraint @QueryParam("query") final String query,
+            @Size(max = 11) @ParamConstraint @HeaderParam("header") final String header,
+            @Size(max = 11) @ParamConstraint @CookieParam("cookie") final String cookie,
+            @Size(max = 11) @ParamConstraint @FormParam("form") final String form) {
+
+        return getInputParamsResponse(path, matrix, query, header, cookie, form);
+    }
+
+    @POST
+    @Path("multipleParam/{path: .*}")
+    @Consumes("application/x-www-form-urlencoded")
+    public String validateParametersMultipleConstraint(
+            @MultipleParamConstraint @PathParam("path") final String path,
+            @MultipleParamConstraint @MatrixParam("matrix") final String matrix,
+            @MultipleParamConstraint @QueryParam("query") final String query,
+            @MultipleParamConstraint @HeaderParam("header") final String header,
+            @MultipleParamConstraint @CookieParam("cookie") final String cookie,
+            @MultipleParamConstraint @FormParam("form") final String form) {
+
+        return getInputParamsResponse(path, matrix, query, header, cookie, form);
+    }
+
+    @POST
+    @Path("emptyBeanParam")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    public ContactBean validateEmptyBeanParamConstraint(@NotNull final ContactBean bean) {
+        return bean;
+    }
+
+    @POST
+    @Path("validBeanParam")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    public ContactBean validateValidBeanParamConstraint(@NotNull @Valid final ContactBean bean) {
+        return bean;
+    }
+
+    @POST
+    @Path("customBeanParam")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    public ContactBean validateCustomBeanParamConstraint(@OneContact final ContactBean bean) {
+        return bean;
+    }
+
+    @POST
+    @Path("emptyBeanResponse")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    @NotNull
+    public ContactBean validateEmptyBeanResponseConstraint(final ContactBean bean) {
+        return bean;
+    }
+
+    @POST
+    @Path("validBeanResponse")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    @NotNull
+    @Valid
+    public ContactBean validateValidBeanResponseConstraint(final ContactBean bean) {
+        return bean;
+    }
+
+    @POST
+    @Path("validBeanWrappedInResponse")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    @NotNull
+    @Valid
+    public Response validateValidBeanWrappedInResponseConstraint(final ContactBean bean) {
+        return Response.ok(bean).type("application/contactBean").build();
+    }
+
+    @POST
+    @Path("customBeanResponse")
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    @OneContact
+    public ContactBean validateCustomBeanResponseConstraint(final ContactBean bean) {
+        return bean;
+    }
+
+    @GET
+    @Path("invalidContext")
+    @SuppressWarnings("UnusedParameters")
+    public Response invalidContext(@Null @Context final Request request) {
+        return Response.status(500).build();
+    }
+
+    @Path("sub/validResourceContextInstance")
+    public BasicSubResource getSubResourceValidResourceContextInstance() {
+        return resourceContext.initResource(new BasicSubResource(resourceContext));
+    }
+
+    @Path("sub/nullResourceContextInstance")
+    public BasicSubResource getSubResourceNullResourceContextInstance() {
+        return resourceContext.initResource(new BasicSubResource(null));
+    }
+
+    @Path("sub/nullResourceContextClass")
+    public BasicSubResource getSubResourceNullResourceContextClass() {
+        return resourceContext.getResource(BasicSubResource.class);
+    }
+
+    @Path("sub/wrong")
+    public BasicBadSubResource getWrongSubResource() {
+        return resourceContext.getResource(BasicBadSubResource.class);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicSubResource.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicSubResource.java
new file mode 100644
index 0000000..f5c0e14
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicSubResource.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+
+import org.hibernate.validator.constraints.Email;
+
+/**
+ * @author Michal Gajdos
+ */
+@NonEmptyNames
+@ValidateOnExecution(type = ExecutableType.ALL)
+public class BasicSubResource {
+
+    @NotNull
+    @FormParam("firstName")
+    private String firstName;
+
+    @NotNull
+    @FormParam("lastName")
+    private String lastName;
+
+    private String email;
+
+    /**
+     * Note: Constructor input parameter should not be validated.
+     */
+    @SuppressWarnings("UnusedParameters")
+    public BasicSubResource(@NotNull @Context final ResourceContext resourceContext) {
+    }
+
+    @FormParam("email")
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    @NotNull
+    @Email(regexp = "[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}")
+    public String getEmail() {
+        return email;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    @POST
+    @Consumes("application/x-www-form-urlencoded")
+    @Produces("application/contactBean")
+    @Valid
+    public ContactBean postContactValidationBean() {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setName(firstName + " " + lastName);
+        contactBean.setEmail(getEmail());
+        return contactBean;
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicValidationTest.java
new file mode 100644
index 0000000..954d472
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicValidationTest.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class BasicValidationTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        final ResourceConfig resourceConfig = new ResourceConfig(BasicResource.class);
+
+        resourceConfig.register(ContactBeanProvider.class);
+        resourceConfig.register(MoxyXmlFeature.class);
+
+        resourceConfig.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
+
+        return resourceConfig;
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        super.configureClient(config);
+        config.register(ContactBeanProvider.class);
+        config.register(MoxyXmlFeature.class);
+    }
+
+    @Consumes("application/contactBean")
+    @Produces("application/contactBean")
+    @Provider
+    public static class ContactBeanProvider implements MessageBodyReader<ContactBean>, MessageBodyWriter<ContactBean> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type.equals(ContactBean.class);
+        }
+
+        @Override
+        public ContactBean readFrom(Class<ContactBean> type,
+                                    Type genericType,
+                                    Annotation[] annotations,
+                                    MediaType mediaType,
+                                    MultivaluedMap<String, String> httpHeaders,
+                                    InputStream entityStream) throws IOException, WebApplicationException {
+            try {
+                final ObjectInputStream objectInputStream = new ObjectInputStream(entityStream);
+                return (ContactBean) objectInputStream.readObject();
+            } catch (Exception e) {
+                // do nothing.
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type.equals(ContactBean.class);
+        }
+
+        @Override
+        public long getSize(ContactBean contactBean,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(ContactBean contactBean,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            try {
+                new ObjectOutputStream(entityStream).writeObject(contactBean);
+            } catch (Exception e) {
+                // do nothing.
+            }
+        }
+    }
+
+    private static final class ParamBean {
+
+        @HeaderParam("header")
+        private String headerParam;
+
+        @PathParam("path")
+        private String pathParam;
+
+        @MatrixParam("matrix")
+        private String matrixParam;
+
+        @QueryParam("query")
+        private String queryParam;
+
+        @CookieParam("cookie")
+        private String cookie;
+
+        @FormParam("form")
+        private String formParam;
+
+        public ParamBean() {
+        }
+
+        public String getCookie() {
+            return cookie;
+        }
+
+        public void setCookie(String cookie) {
+            this.cookie = cookie;
+        }
+
+        public String getFormParam() {
+            return formParam;
+        }
+
+        public void setFormParam(String formParam) {
+            this.formParam = formParam;
+        }
+
+        public String getHeaderParam() {
+            return headerParam;
+        }
+
+        public void setHeaderParam(String headerParam) {
+            this.headerParam = headerParam;
+        }
+
+        public String getMatrixParam() {
+            return matrixParam;
+        }
+
+        public void setMatrixParam(String matrixParam) {
+            this.matrixParam = matrixParam;
+        }
+
+        public String getPathParam() {
+            return pathParam;
+        }
+
+        public void setPathParam(String pathParam) {
+            this.pathParam = pathParam;
+        }
+
+        public String getQueryParam() {
+            return queryParam;
+        }
+
+        public void setQueryParam(String queryParam) {
+            this.queryParam = queryParam;
+        }
+
+        @Override
+        public String toString() {
+            return "Bean{"
+                    + "cookie='" + cookie + '\''
+                    + ", formParam='" + formParam + '\''
+                    + ", headerParam='" + headerParam + '\''
+                    + ", matrixParam='" + matrixParam + '\''
+                    + ", pathParam='" + pathParam + '\''
+                    + ", queryParam='" + queryParam + '\''
+                    + '}';
+        }
+    }
+
+    private ParamBean getDefaultParamBean() {
+        final ParamBean paramBean = new ParamBean();
+        paramBean.setCookie("cookieParam");
+        paramBean.setFormParam("formParam");
+        paramBean.setHeaderParam("headerParam");
+        paramBean.setMatrixParam("matrixParam");
+        paramBean.setPathParam("pathParam");
+        paramBean.setQueryParam("queryParam");
+        return paramBean;
+    }
+
+    private Response testInputParams(final String path, final ParamBean paramBean) throws Exception {
+        final Form form = new Form();
+        form.asMap().put("form", Arrays.asList(paramBean.getFormParam()));
+
+        WebTarget target = target("beanvalidation").path(path);
+
+        if (paramBean.getPathParam() != null) {
+            target = target.path(paramBean.getPathParam());
+        } else {
+            target = target.path("/");
+        }
+
+        Invocation.Builder request = target
+                .matrixParam("matrix", paramBean.getMatrixParam())
+                .queryParam("query", paramBean.getQueryParam())
+                .request();
+
+        if (paramBean.getHeaderParam() != null) {
+            request = request.header("header", paramBean.getHeaderParam());
+        }
+
+        if (paramBean.getCookie() != null) {
+            request = request.cookie("cookie", paramBean.getCookie());
+        }
+
+        return request.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+    }
+
+    @Test
+    public void testInputParamsBasicConstraintsPositive() throws Exception {
+        final Response response = testInputParams("basicParam", getDefaultParamBean());
+
+        assertEquals(200, response.getStatus());
+        assertEquals("pathParam_matrixParam_queryParam_headerParam_cookieParam_formParam", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInputParamsBasicConstraintsNegative() throws Exception {
+        final Response response = testInputParams("basicParam", new ParamBean());
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg1")); // @MatrixParam
+        assertTrue(message.contains("arg2")); // @QueryParam
+        assertTrue(message.contains("arg3")); // @HeaderParam
+        assertTrue(message.contains("arg4")); // @CookieParam
+        assertTrue(message.contains("arg5")); // @FormParam
+
+        assertFalse(message.contains("arg0")); // @PathParam
+        assertFalse(message.contains("arg6")); // @Context
+    }
+
+    @Test
+    public void testInputParamsDefaultBasicConstraintsPositive() throws Exception {
+        final Response response = testInputParams("basicDefaultParam", new ParamBean());
+
+        assertEquals(200, response.getStatus());
+        assertEquals("_matrixParam_queryParam_headerParam_cookieParam_formParam", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInputParamsCustomConstraintsPositive() throws Exception {
+        final Response response = testInputParams("customParam", getDefaultParamBean());
+
+        assertEquals(200, response.getStatus());
+        assertEquals("pathParam_matrixParam_queryParam_headerParam_cookieParam_formParam", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInputParamsCustomConstraintsNegative() throws Exception {
+        final Response response = testInputParams("customParam", new ParamBean());
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0")); // @PathParam
+        assertTrue(message.contains("arg1")); // @MatrixParam
+        assertTrue(message.contains("arg2")); // @QueryParam
+        assertTrue(message.contains("arg3")); // @HeaderParam
+        assertTrue(message.contains("arg4")); // @CookieParam
+        assertTrue(message.contains("arg5")); // @FormParam
+    }
+
+    @Test
+    public void testInputParamsMixedConstraintsPositive() throws Exception {
+        final Response response = testInputParams("mixedParam", getDefaultParamBean());
+
+        assertEquals(200, response.getStatus());
+        assertEquals("pathParam_matrixParam_queryParam_headerParam_cookieParam_formParam", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInputParamsMixedConstraintsNegative() throws Exception {
+        final Response response = testInputParams("mixedParam", new ParamBean());
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0")); // @PathParam
+        assertTrue(message.contains("arg1")); // @MatrixParam
+        assertTrue(message.contains("arg2")); // @QueryParam
+        assertTrue(message.contains("arg3")); // @HeaderParam
+        assertTrue(message.contains("arg4")); // @CookieParam
+        assertTrue(message.contains("arg5")); // @FormParam
+    }
+
+    @Test
+    public void testInputParamsMixedConstraintsNegativeTooLong() throws Exception {
+        final ParamBean defaultParamBean = getDefaultParamBean();
+        final String formParam = defaultParamBean.getFormParam();
+        defaultParamBean.setFormParam(formParam + formParam);
+
+        final Response response = testInputParams("mixedParam", defaultParamBean);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertFalse(message.contains("arg0")); // @PathParam
+        assertFalse(message.contains("arg1")); // @MatrixParam
+        assertFalse(message.contains("arg2")); // @QueryParam
+        assertFalse(message.contains("arg3")); // @HeaderParam
+        assertFalse(message.contains("arg4")); // @CookieParam
+        assertTrue(message.contains("arg5")); // @FormParam
+    }
+
+    @Test
+    public void testInputParamsMultipleConstraintsPositive() throws Exception {
+        final Response response = testInputParams("multipleParam", getDefaultParamBean());
+
+        assertEquals(200, response.getStatus());
+        assertEquals("pathParam_matrixParam_queryParam_headerParam_cookieParam_formParam", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInputParamsMultipleConstraintsNegative() throws Exception {
+        final Response response = testInputParams("multipleParam", new ParamBean());
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0")); // @PathParam
+        assertTrue(message.contains("arg1")); // @MatrixParam
+        assertTrue(message.contains("arg2")); // @QueryParam
+        assertTrue(message.contains("arg3")); // @HeaderParam
+        assertTrue(message.contains("arg4")); // @CookieParam
+        assertTrue(message.contains("arg5")); // @FormParam
+    }
+
+    @Test
+    public void testInputParamsMultipleConstraintsNegativeTooLong() throws Exception {
+        final ParamBean defaultParamBean = getDefaultParamBean();
+        final String formParam = defaultParamBean.getFormParam();
+        defaultParamBean.setFormParam(formParam + formParam);
+
+        final Response response = testInputParams("mixedParam", defaultParamBean);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertFalse(message.contains("arg0")); // @PathParam
+        assertFalse(message.contains("arg1")); // @MatrixParam
+        assertFalse(message.contains("arg2")); // @QueryParam
+        assertFalse(message.contains("arg3")); // @HeaderParam
+        assertFalse(message.contains("arg4")); // @CookieParam
+        assertTrue(message.contains("arg5")); // @FormParam
+    }
+
+    private Response testBean(final String path, final ContactBean contactBean) {
+        return target("beanvalidation")
+                .path(path)
+                .request("application/contactBean").post(Entity.entity(contactBean, "application/contactBean"));
+    }
+
+    @Test
+    public void testEmptyBeanParamPositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        final Response response = testBean("emptyBeanParam", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testEmptyBeanParamNegative() throws Exception {
+        final Response response = testBean("emptyBeanParam", null);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0"));
+    }
+
+    @Test
+    public void testValidBeanParamPositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setName("Jersey");
+        contactBean.setEmail("jersey@example.com");
+        final Response response = testBean("validBeanParam", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testValidBeanParamNegative() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        // Add value to pass @OneContact constraint but fails on @Pattern constraint defined on getter.
+        contactBean.setPhone("12");
+        final Response response = testBean("validBeanParam", contactBean);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0"));
+    }
+
+    @Test
+    public void testCustomBeanParamPositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setEmail("jersey@example.com");
+
+        final Response response = testBean("customBeanParam", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testCustomBeanParamNegative() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setEmail("jersey@example.com");
+        contactBean.setPhone("134539");
+
+        final Response response = testBean("customBeanParam", contactBean);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0"));
+    }
+
+    @Test
+    public void testEmptyBeanResponsePositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        final Response response = testBean("emptyBeanResponse", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testEmptyBeanResponseNegative() throws Exception {
+        final Response response = testBean("emptyBeanResponse", null);
+
+        assertEquals(500, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("return value"));
+    }
+
+    @Test
+    public void testValidBeanResponsePositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setName("Jersey");
+        contactBean.setEmail("jersey@example.com");
+        final Response response = testBean("validBeanResponse", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testValidBeanResponseNegative() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        final Response response = testBean("validBeanResponse", contactBean);
+
+        assertEquals(500, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("return value"));
+    }
+
+    @Test
+    public void testValidBeanWrappedInResponsePositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setName("Jersey");
+        contactBean.setEmail("jersey@example.com");
+        final Response response = testBean("validBeanWrappedInResponse", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testValidBeanWrappedInResponseNegative() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        final Response response = testBean("validBeanWrappedInResponse", contactBean);
+
+        assertEquals(500, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("return value"));
+    }
+
+    @Test
+    public void testCustomBeanResponsePositive() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setEmail("jersey@example.com");
+        final Response response = testBean("customBeanResponse", contactBean);
+
+        assertEquals(contactBean, response.readEntity(ContactBean.class));
+    }
+
+    @Test
+    public void testCustomBeanResponseNegative() throws Exception {
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setEmail("jersey@example.com");
+        contactBean.setPhone("134539");
+
+        final Response response = testBean("customBeanResponse", contactBean);
+
+        assertEquals(500, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("return value"));
+    }
+
+    @Test
+    public void testInvalidContextValidation() throws Exception {
+        final Response response = target("beanvalidation").path("invalidContext").request().get();
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("arg0"));
+    }
+
+    private Response testSubResource(final String path, final Form form) throws Exception {
+        return target("beanvalidation")
+                .path("sub")
+                .path(path)
+                .request()
+                .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+    }
+
+    private void testSubResourcePositive(final String path) throws Exception {
+        testSubResource(path, false);
+    }
+
+    private void testSubResource(final String path, final boolean omitEmail) throws Exception {
+        final Form form = new Form();
+        form.asMap().put("firstName", Arrays.asList("Jersey"));
+        form.asMap().put("lastName", Arrays.asList("JAX-RS"));
+        if (!omitEmail) {
+            form.asMap().put("email", Arrays.asList("jersey@example.com"));
+        }
+
+        final ContactBean contactBean = new ContactBean();
+        contactBean.setName("Jersey JAX-RS");
+        contactBean.setEmail("jersey@example.com");
+
+        final Response response = testSubResource(path, form);
+
+        if (omitEmail) {
+            assertEquals(400, response.getStatus());
+
+            final String message = response.readEntity(String.class);
+            assertTrue(message.contains("email"));
+        } else {
+            assertEquals(200, response.getStatus());
+            assertEquals(contactBean, response.readEntity(ContactBean.class));
+        }
+    }
+
+    private void testSubResourceNegative(final String path) throws Exception {
+        final Form form = new Form();
+        form.asMap().put("foo", Arrays.asList("bar"));
+
+        final Response response = testSubResource(path, form);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("firstName"));
+        assertTrue(message.contains("lastName"));
+        assertTrue(message.contains("email"));
+    }
+
+    @Test
+    public void testSubResourceValidResourceContextPositive() throws Exception {
+        testSubResourcePositive("validResourceContextInstance");
+    }
+
+    @Test
+    public void testSubResourceNullResourceContextPositive() throws Exception {
+        testSubResourcePositive("nullResourceContextInstance");
+    }
+
+    @Test
+    public void testSubResourceNullResourceContextClassPositive() throws Exception {
+        testSubResourcePositive("nullResourceContextClass");
+    }
+
+    @Test
+    public void testSubResourceValidResourceContextNegative() throws Exception {
+        testSubResourceNegative("validResourceContextInstance");
+    }
+
+    @Test
+    public void testSubResourceNullResourceContextNegative() throws Exception {
+        testSubResourceNegative("nullResourceContextInstance");
+    }
+
+    @Test
+    public void testSubResourceEmptyNames() throws Exception {
+        final Form form = new Form();
+        form.asMap().put("firstName", Arrays.asList(""));
+        form.asMap().put("lastName", Arrays.asList(""));
+
+        final Response response = testSubResource("nullResourceContextInstance", form);
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("at least one of the names is empty"));
+    }
+
+    @Test
+    public void testSubResourcePropertyNegative() throws Exception {
+        testSubResource("validResourceContextInstance", true);
+    }
+
+    @Test
+    public void testWrongSubResourceNegative() throws Exception {
+        final Response response = target("beanvalidation")
+                .path("sub/wrong")
+                .request()
+                .get();
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+        assertTrue(message.contains("resourceContext"));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ContactBean.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ContactBean.java
new file mode 100644
index 0000000..bad13ae
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ContactBean.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+
+import org.hibernate.validator.constraints.Email;
+import org.hibernate.validator.constraints.NotBlank;
+
+/**
+ * @author Michal Gajdos
+ */
+@OneContact
+public class ContactBean implements Serializable {
+
+    @Email(regexp = "[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}")
+    private String email;
+
+    private String phone;
+
+    @NotBlank
+    private String name;
+
+    @NotNull(groups = {Extended.class})
+    private String city;
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    @Pattern(regexp = "[0-9]{3,9}")
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(final String phone) {
+        this.phone = phone;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ContactBean)) {
+            return false;
+        }
+
+        final ContactBean that = (ContactBean) o;
+
+        if (email != null ? !email.equals(that.email) : that.email != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+        if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = email != null ? email.hashCode() : 0;
+        result = 31 * result + (phone != null ? phone.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ContactBean{"
+                + "email='" + email + '\''
+                + ", phone='" + phone + '\''
+                + ", name='" + name + '\''
+                + '}';
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomBean.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomBean.java
new file mode 100644
index 0000000..fba9532
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomBean.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+/**
+ * @author Michal Gajdos
+ */
+@XmlRootElement
+@CustomValidation
+public class CustomBean {
+
+    @NotNull
+    private String path;
+
+    private boolean validate = true;
+
+    public void setPath(final String path) {
+        this.path = path;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    @XmlTransient
+    public boolean isValidate() {
+        return validate;
+    }
+
+    public void setValidate(final boolean validate) {
+        this.validate = validate;
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java
new file mode 100644
index 0000000..de5aefb
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+
+import javax.validation.MessageInterpolator;
+import javax.validation.ParameterNameProvider;
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import javax.validation.Valid;
+import javax.validation.Validation;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.validation.ValidationConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.eclipse.persistence.jaxb.BeanValidationMode;
+import org.eclipse.persistence.jaxb.MarshallerProperties;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class CustomConfigValidationTest extends JerseyTest {
+
+    @javax.ws.rs.Path("customconfigvalidation/{path: .*}")
+    public static class CustomConfigResource {
+
+        @POST
+        @Consumes("application/xml")
+        @Produces("application/xml")
+        @NotNull
+        @Valid
+        public CustomBean post(@PathParam("path") final String path, final CustomBean beanParameter,
+                               @Size(min = 5) @HeaderParam("myHeader") final String header) {
+            if ("".equals(path)) {
+                beanParameter.setPath(null);
+                beanParameter.setValidate(false);
+            } else {
+                beanParameter.setPath(path);
+                beanParameter.setValidate(true);
+            }
+            return beanParameter;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        final ResourceConfig resourceConfig = new ResourceConfig(CustomConfigResource.class);
+
+        // Turn off BV in MOXy otherwise the entities on server would be validated at incorrect times.
+        resourceConfig.register(moxyXmlFeature());
+        resourceConfig.register(ValidationConfigurationContextResolver.class);
+
+        resourceConfig.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
+
+        return resourceConfig;
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        super.configureClient(config);
+
+        // Turn off BV in MOXy otherwise the entities on client would be validated as well.
+        config.register(moxyXmlFeature());
+    }
+
+    private MoxyXmlFeature moxyXmlFeature() {
+        return new MoxyXmlFeature(new HashMap<String, Object>() {{
+                    put(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE);
+                }},
+                Thread.currentThread().getContextClassLoader(),
+                false
+        );
+    }
+
+    @Test
+    public void testPositive() throws Exception {
+        final Response response = target("customconfigvalidation")
+                .path("ok")
+                .request()
+                .header("myHeader", "12345")
+                .post(Entity.entity(new CustomBean(), MediaType.APPLICATION_XML_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("ok", response.readEntity(CustomBean.class).getPath());
+    }
+
+    @Test
+    public void testParameterNameWithInterpolator() throws Exception {
+        final Response response = target("customconfigvalidation")
+                .path("ok")
+                .request()
+                .header("myHeader", "1234")
+                .post(Entity.entity(new CustomBean(), MediaType.APPLICATION_XML_TYPE));
+
+        assertEquals(400, response.getStatus());
+
+        final String message = response.readEntity(String.class);
+
+        assertFalse(message.contains("arg2"));
+        assertTrue(message.contains("header"));
+        assertFalse(message.contains("size must be between"));
+        assertTrue(message.contains("message"));
+    }
+
+    @Test
+    public void testTraversableResolver() throws Exception {
+        final Response response = target("customconfigvalidation/")
+                .request()
+                .header("myHeader", "12345")
+                .post(Entity.entity(new CustomBean(), MediaType.APPLICATION_XML_TYPE));
+
+        assertEquals(200, response.getStatus());
+        // return value passed validation because of "corrupted" traversableresolver
+        assertEquals(null, response.readEntity(CustomBean.class).getPath());
+    }
+
+    public static class ValidationConfigurationContextResolver implements ContextResolver<ValidationConfig> {
+
+        private final ValidationConfig config;
+
+        public ValidationConfigurationContextResolver() {
+            config = new ValidationConfig();
+
+            // ConstraintValidatorFactory is set by default.
+            config.messageInterpolator(new CustomMessageInterpolator());
+            config.parameterNameProvider(new CustomParameterNameProvider());
+            config.traversableResolver(new CustomTraversableResolver());
+        }
+
+        @Override
+        public ValidationConfig getContext(final Class<?> type) {
+            return ValidationConfig.class.isAssignableFrom(type) ? config : null;
+        }
+    }
+
+    private static class CustomMessageInterpolator implements MessageInterpolator {
+
+        @Override
+        public String interpolate(final String messageTemplate, final Context context) {
+            return "message";
+        }
+
+        @Override
+        public String interpolate(final String messageTemplate, final Context context, final Locale locale) {
+            return "localized message";
+        }
+    }
+
+    private static class CustomParameterNameProvider implements ParameterNameProvider {
+
+        private final ParameterNameProvider nameProvider;
+
+        public CustomParameterNameProvider() {
+            nameProvider = Validation.byDefaultProvider().configure().getDefaultParameterNameProvider();
+        }
+
+        @Override
+        public List<String> getParameterNames(final Constructor<?> constructor) {
+            return nameProvider.getParameterNames(constructor);
+        }
+
+        @Override
+        public List<String> getParameterNames(final Method method) {
+            try {
+                final Method post = CustomConfigResource.class.getMethod("post", String.class, CustomBean.class, String.class);
+
+                if (method.equals(post)) {
+                    return Arrays.asList("path", "beanParameter", "header");
+                }
+            } catch (final NoSuchMethodException e) {
+                // Do nothing.
+            }
+            return nameProvider.getParameterNames(method);
+        }
+    }
+
+    private static class CustomTraversableResolver implements TraversableResolver {
+
+        @Override
+        public boolean isReachable(final Object traversableObject,
+                                   final Path.Node traversableProperty,
+                                   final Class<?> rootBeanType,
+                                   final Path pathToTraversableObject,
+                                   final ElementType elementType) {
+            return false;
+        }
+
+        @Override
+        public boolean isCascadable(final Object traversableObject,
+                                    final Path.Node traversableProperty,
+                                    final Class<?> rootBeanType,
+                                    final Path pathToTraversableObject,
+                                    final ElementType elementType) {
+            return false;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomValidation.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomValidation.java
new file mode 100644
index 0000000..67a2d23
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomValidation.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Payload;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Michal Gajdos
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@NotNull
+@Constraint(validatedBy = CustomValidation.Validator.class)
+public @interface CustomValidation {
+
+    String message() default "client should never see this";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+    public static class Validator implements ConstraintValidator<CustomValidation, CustomBean> {
+
+        @Context
+        private UriInfo uriInfo;
+
+        @Override
+        public void initialize(final CustomValidation constraintAnnotation) {
+        }
+
+        @Override
+        public boolean isValid(final CustomBean bean, final ConstraintValidatorContext context) {
+            // !bean.isValidate() - to make sure this validation passes for CustomConfigValidationTest#testTraversableResolver
+            // (TraversableResolver is not invoked for ElementType.TYPE anymore).
+            return !bean.isValidate() || uriInfo.getPathParameters().getFirst("path").equals(bean.getPath());
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/EntityInheritanceValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/EntityInheritanceValidationTest.java
new file mode 100644
index 0000000..e505374
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/EntityInheritanceValidationTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2014, 2018 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.validation;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.hibernate.validator.constraints.NotBlank;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests to ensure that validation constraints on a superclass are validated as well.
+ *
+ * @author Michal Gajdos
+ */
+public class EntityInheritanceValidationTest extends JerseyTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        @Produces("application/json")
+        public Entity post(@Valid final Entity entity) {
+            return entity;
+        }
+    }
+
+    public static class AbstractEntity {
+
+        private String text;
+
+        public AbstractEntity() {
+        }
+
+        public AbstractEntity(final String text) {
+            this.text = text;
+        }
+
+        @NotNull
+        @NotBlank
+        public String getText() {
+            return text;
+        }
+
+        public void setText(final String text) {
+            this.text = text;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            final AbstractEntity that = (AbstractEntity) o;
+
+            if (!text.equals(that.text)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return text.hashCode();
+        }
+    }
+
+    public static class Entity extends AbstractEntity {
+
+        private Integer number;
+
+        public Entity() {
+        }
+
+        public Entity(final String text, final Integer number) {
+            super(text);
+            this.number = number;
+        }
+
+        @Min(12)
+        @Max(14)
+        @NotNull
+        public Integer getNumber() {
+            return number;
+        }
+
+        public void setNumber(final Integer number) {
+            this.number = number;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            if (!super.equals(o)) {
+                return false;
+            }
+
+            final Entity entity = (Entity) o;
+
+            if (!number.equals(entity.number)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = super.hashCode();
+            result = 31 * result + number.hashCode();
+            return result;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class)
+                .register(JacksonFeature.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(JacksonFeature.class);
+    }
+
+    @Test
+    public void testEntityInheritance() throws Exception {
+        final Entity entity = new Entity("foo", 13);
+        final Response response = target().request().post(javax.ws.rs.client.Entity.json(entity));
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(Entity.class), is(entity));
+    }
+
+    @Test
+    public void testEntityInheritanceBlankText() throws Exception {
+        final Response response = target().request().post(javax.ws.rs.client.Entity.json(new Entity("", 13)));
+
+        assertThat(response.getStatus(), is(400));
+    }
+
+    @Test
+    public void testEntityInheritanceInvalidNumber() throws Exception {
+        final Response response = target().request().post(javax.ws.rs.client.Entity.json(new Entity("foo", 23)));
+
+        assertThat(response.getStatus(), is(400));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/Extended.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/Extended.java
new file mode 100644
index 0000000..529fa09
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/Extended.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+/**
+ * @author Michal Gajdos
+ */
+public interface Extended {
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidation.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidation.java
new file mode 100644
index 0000000..4ebc5d7
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidation.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Payload;
+
+/**
+ * @author Michal Gajdos
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = FieldPropertyValidation.Validator.class)
+public @interface FieldPropertyValidation {
+
+    String message() default "one or more fields are not valid";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+    String[] elements() default {};
+
+    public class Validator implements ConstraintValidator<FieldPropertyValidation, FieldPropertyValidationResource.SubResource> {
+
+        private List<String> properties;
+
+        @Override
+        public void initialize(final FieldPropertyValidation annotation) {
+            this.properties = Arrays.asList(annotation.elements());
+        }
+
+        @Override
+        public boolean isValid(final FieldPropertyValidationResource.SubResource bean,
+                               final ConstraintValidatorContext constraintValidatorContext) {
+            boolean result = true;
+
+            for (final String property : properties) {
+                if ("fieldAndClass".equals(property)) {
+                    result &= bean.fieldAndClass != null;
+                } else if ("propertyAndClass".equals(property)) {
+                    result &= bean.getPropertyAndClass() != null;
+                } else if ("propertyGetterAndClass".equals(property)) {
+                    result &= bean.getPropertyGetterAndClass() != null;
+                }
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationResource.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationResource.java
new file mode 100644
index 0000000..ea3c896
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationResource.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("fieldPropertyValidationResource")
+public class FieldPropertyValidationResource {
+
+    @Path("valid")
+    public SubResource valid() {
+        return new SubResource("valid", "valid", "valid", "valid", "valid", "valid");
+    }
+
+    @Path("invalidPropertyGetterAndClassNull")
+    public SubResource invalidPropertyGetterAndClassNull() {
+        return new SubResource("valid", "valid", "valid", "valid", "valid", null);
+    }
+
+    @Path("invalidPropertyGetterAndClassLong")
+    public SubResource invalidPropertyGetterAndClassLong() {
+        return new SubResource("valid", "valid", "valid", "valid", "valid", "valid-valid");
+    }
+
+    @Path("invalidPropertyAndClassNull")
+    public SubResource invalidPropertyAndClassNull() {
+        return new SubResource("valid", "valid", "valid", "valid", null, "valid");
+    }
+
+    @Path("invalidFieldAndClassNull")
+    public SubResource invalidFieldAndClassNull() {
+        return new SubResource("valid", "valid", "valid", null, "valid", "valid");
+    }
+
+    @Path("invalidPropertyGetterNull")
+    public SubResource invalidPropertyGetterNull() {
+        return new SubResource("valid", "valid", null, "valid", "valid", "valid");
+    }
+
+    @Path("invalidPropertyGetterLong")
+    public SubResource invalidPropertyGetterLong() {
+        return new SubResource("valid", "valid", "valid-valid", "valid", "valid", "valid");
+    }
+
+    @Path("invalidPropertyNull")
+    public SubResource invalidPropertyNull() {
+        return new SubResource("valid", null, "valid", "valid", "valid", "valid");
+    }
+
+    @Path("invalidFieldNull")
+    public SubResource invalidFieldNull() {
+        return new SubResource(null, "valid", "valid", null, "valid", "valid");
+    }
+
+    @FieldPropertyValidation(elements = {"fieldAndClass", "propertyAndClass", "propertyGetterAndClass"})
+    public static class SubResource {
+
+        @NotNull
+        @Size(min = 5)
+        final String field;
+
+        @NotNull
+        @Size(min = 5)
+        final String property;
+
+        @NotNull
+        @Size(min = 5)
+        final String propertyGetter;
+
+        final String fieldAndClass;
+
+        final String propertyAndClass;
+
+        final String propertyGetterAndClass;
+
+        public SubResource(final String field, final String property, final String propertyAndGetter,
+                           final String fieldAndClass, final String propertyAndClass, final String propertyGetterAndClass) {
+            this.field = field;
+            this.property = property;
+            this.propertyGetter = propertyAndGetter;
+            this.fieldAndClass = fieldAndClass;
+            this.propertyAndClass = propertyAndClass;
+            this.propertyGetterAndClass = propertyGetterAndClass;
+        }
+
+        public String getProperty() {
+            return property;
+        }
+
+        @Size(max = 5)
+        public String getPropertyGetter() {
+            return propertyGetter;
+        }
+
+        public String getPropertyAndClass() {
+            return propertyAndClass;
+        }
+
+        @Size(max = 5)
+        public String getPropertyGetterAndClass() {
+            return propertyGetterAndClass;
+        }
+
+        @GET
+        public String method() {
+            return "ok";
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationTest.java
new file mode 100644
index 0000000..6750769
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class FieldPropertyValidationTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(FieldPropertyValidationResource.class);
+    }
+
+    @Test
+    public void testValid() throws Exception {
+        _test("valid", 200);
+    }
+
+    @Test
+    public void testInvalidPropertyGetterAndClassNull() throws Exception {
+        _test("invalidPropertyGetterAndClassNull", 400);
+    }
+
+    @Test
+    public void testInvalidPropertyGetterAndClassLong() throws Exception {
+        _test("invalidPropertyGetterAndClassLong", 400);
+    }
+
+    @Test
+    public void testInvalidPropertyAndClassNull() throws Exception {
+        _test("invalidPropertyAndClassNull", 400);
+    }
+
+    @Test
+    public void testInvalidFieldAndClassNull() throws Exception {
+        _test("invalidFieldAndClassNull", 400);
+    }
+
+    @Test
+    public void testInvalidPropertyGetterNull() throws Exception {
+        _test("invalidPropertyGetterNull", 400);
+    }
+
+    @Test
+    public void testInvalidPropertyGetterLong() throws Exception {
+        _test("invalidPropertyGetterLong", 400);
+    }
+
+    @Test
+    public void testInvalidPropertyNull() throws Exception {
+        _test("invalidPropertyNull", 400);
+    }
+
+    @Test
+    public void testInvalidFieldNull() throws Exception {
+        _test("invalidFieldNull", 400);
+    }
+
+    private void _test(final String path, final int status) {
+        final Response response = target("fieldPropertyValidationResource")
+                .path(path)
+                .request()
+                .get();
+
+        assertThat(response.getStatus(), equalTo(status));
+
+        if (status == 200) {
+            assertThat(response.readEntity(String.class), equalTo("ok"));
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/InheritanceValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/InheritanceValidationTest.java
new file mode 100644
index 0000000..a8fa2c8
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/InheritanceValidationTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class InheritanceValidationTest extends JerseyTest {
+
+    public static interface ResourceNumberInterface<T extends Number> {
+
+        @POST
+        @Min(0)
+        @NotNull
+        public T post(@NotNull @Max(100) final T value);
+    }
+
+    public static interface ResourceStringInterface {
+
+        @Min(-50)
+        public String post(@Max(50) final String value);
+    }
+
+    @Path("/")
+    public static class ResourceNumberString implements ResourceNumberInterface<Integer>, ResourceStringInterface {
+
+        @Override
+        public Integer post(final Integer value) {
+            return value;
+        }
+
+        @POST
+        @Path("string")
+        @Override
+        public String post(final String value) {
+            return value;
+        }
+    }
+
+    @Path("/sub")
+    public static class SubClassResourceNumberString extends ResourceNumberString {
+
+        @Override
+        public Integer post(final Integer value) {
+            return value;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ResourceNumberString.class, SubClassResourceNumberString.class);
+    }
+
+    @Test
+    public void testValidateNumberPositive() throws Exception {
+        _test(75, 200);
+    }
+
+    @Test
+    public void testValidateNumberInputNegative() throws Exception {
+        _test(150, 400);
+    }
+
+    @Test
+    public void testValidateStringPositive() throws Exception {
+        _test("string", "25", 200);
+    }
+
+    @Test
+    public void testValidateStringInputNegative() throws Exception {
+        _test("string", "150", 400);
+    }
+
+    @Test
+    public void testValidateNumberSubClassPositive() throws Exception {
+        _test("sub", 75, 200);
+    }
+
+    @Test
+    public void testValidateNumberInputSubClassNegative() throws Exception {
+        _test("sub", 150, 400);
+    }
+
+    @Test
+    public void testValidateStringSubClassPositive() throws Exception {
+        _test("sub/string", "25", 200);
+    }
+
+    @Test
+    public void testValidateStringInputSubClassNegative() throws Exception {
+        _test("sub/string", "150", 400);
+    }
+
+    @Test
+    public void testValidateNumberResponseNegative() throws Exception {
+        _test(-150, 500);
+    }
+
+    @Test
+    public void testValidateStringResponseNegative() throws Exception {
+        _test("string", "-150", 500);
+    }
+
+    @Test
+    public void testValidateNumberResponseSubClassNegative() throws Exception {
+        _test("sub", -150, 500);
+    }
+
+    @Test
+    public void testValidateStringResponseSubClassNegative() throws Exception {
+        _test("sub/string", "-150", 500);
+    }
+
+    private void _test(final Object value, final int responseStatus) {
+        _test("", value, responseStatus);
+    }
+
+    private void _test(final String path, final Object value, final int responseStatus) {
+        final Response response = target(path).request().post(Entity.text(value));
+
+        assertThat("Wrong response.", response.getStatus(), equalTo(responseStatus));
+
+        if (responseStatus == 200) {
+            assertThat("Invalid entity.", response.readEntity(value.getClass()), equalTo(value));
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/MultipleParamConstraint.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/MultipleParamConstraint.java
new file mode 100644
index 0000000..c17af49
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/MultipleParamConstraint.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.Size;
+
+/**
+ * @author Michal Gajdos
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = {})
+@ParamConstraint
+@Size(max = 11)
+public @interface MultipleParamConstraint {
+
+    String message() default "invalid parameter";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/NonEmptyNames.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/NonEmptyNames.java
new file mode 100644
index 0000000..ff29e02
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/NonEmptyNames.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Payload;
+
+/**
+ * @author Michal Gajdos
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = { NonEmptyNames.Validator.class })
+public @interface NonEmptyNames {
+
+    String message() default "{org.glassfish.jersey.tests.e2e.server.validation.NonEmptyNames.message}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+    public class Validator implements ConstraintValidator<NonEmptyNames, BasicSubResource> {
+
+        @Override
+        public void initialize(final NonEmptyNames nonRecursive) {
+        }
+
+        @Override
+        public boolean isValid(final BasicSubResource resource, final ConstraintValidatorContext constraintValidatorContext) {
+            return isValid(resource.getFirstName()) && isValid(resource.getLastName());
+        }
+
+        private boolean isValid(final String name) {
+            // @NotNull checks null value
+            return name == null || !"".equals(name.trim());
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/OneContact.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/OneContact.java
new file mode 100644
index 0000000..c497cb5
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/OneContact.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Payload;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Michal Gajdos
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@NotNull
+@Constraint(validatedBy = OneContact.Validator.class)
+public @interface OneContact {
+
+    String message() default "none or more than one contact";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+    public class Validator implements ConstraintValidator<OneContact, ContactBean> {
+
+        @Override
+        public void initialize(final OneContact nonRecursive) {
+        }
+
+        @Override
+        public boolean isValid(final ContactBean contactBean, final ConstraintValidatorContext constraintValidatorContext) {
+            if (contactBean.getEmail() == null && contactBean.getPhone() == null) {
+                return false;
+            }
+            return !(contactBean.getEmail() != null && contactBean.getPhone() != null);
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ParamConstraint.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ParamConstraint.java
new file mode 100644
index 0000000..6dd262b
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ParamConstraint.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012, 2018 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.validation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+
+/**
+ * Valid: [a-zA-Z]+Param
+ *
+ * @author Michal Gajdos
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = {})
+@NotNull
+@Pattern(regexp = "^[a-zA-Z]+Param$")
+public @interface ParamConstraint {
+
+    String message() default "invalid parameter";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ProgrammaticValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ProgrammaticValidationTest.java
new file mode 100644
index 0000000..3b30812
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ProgrammaticValidationTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.process.Inflector;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Bean Validation tests for programmatically created resources.
+ *
+ * @author Michal Gajdos
+ */
+public class ProgrammaticValidationTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        final Set<Resource> resources = new HashSet<>();
+
+        Resource.Builder resourceBuilder = Resource.builder("instance");
+        resourceBuilder
+                .addMethod("POST")
+                .handledBy(new ValidationInflector());
+        resources.add(resourceBuilder.build());
+
+        resourceBuilder = Resource.builder("class");
+        resourceBuilder
+                .addMethod("POST")
+                .handledBy(ValidationInflector.class);
+        resources.add(resourceBuilder.build());
+
+        try {
+            resourceBuilder = Resource.builder("methodInstanceClass");
+            resourceBuilder
+                    .addMethod("POST")
+                    .handledBy(new ValidationInflector(), ValidationInflector.class.getMethod("get",
+                            ContainerRequestContext.class));
+            resources.add(resourceBuilder.build());
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException();
+        }
+
+        try {
+            resourceBuilder = Resource.builder("methodClassClass");
+            resourceBuilder
+                    .addMethod("POST")
+                    .handledBy(ValidationInflector.class,
+                            ValidationInflector.class.getMethod("get", ContainerRequestContext.class));
+            resources.add(resourceBuilder.build());
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException();
+        }
+
+        try {
+            resourceBuilder = Resource.builder("methodInstanceInterface");
+            resourceBuilder
+                    .addMethod("POST")
+                    .handledBy(new ValidationInflector(), Inflector.class.getMethod("apply", Object.class));
+            resources.add(resourceBuilder.build());
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException();
+        }
+
+        try {
+            resourceBuilder = Resource.builder("methodClassInterface");
+            resourceBuilder
+                    .addMethod("POST")
+                    .handledBy(ValidationInflector.class, Inflector.class.getMethod("apply", Object.class));
+            resources.add(resourceBuilder.build());
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException();
+        }
+
+        return new ResourceConfig().register(LoggingFeature.class).registerResources(resources);
+    }
+
+    @Test
+    public void testInflectorInstance() throws Exception {
+        final Response response = target("instance").request().post(Entity.entity("value", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("value", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInflectorInstanceNegative() throws Exception {
+        final Response response = target("instance").request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testInflectorClass() throws Exception {
+        final Response response = target("class").request().post(Entity.entity("value", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("value", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInflectorClassNegative() throws Exception {
+        final Response response = target("class").request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testInflectorMethodInstanceClass() throws Exception {
+        final Response response = target("methodInstanceClass").request().post(Entity.entity("value", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("value", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInflectorMethodInstanceClassNegative() throws Exception {
+        final Response response = target("methodInstanceClass").request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testInflectorMethodClassClass() throws Exception {
+        final Response response = target("methodClassClass").request().post(Entity.entity("value", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("value", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInflectorMethodClassClassNegative() throws Exception {
+        final Response response = target("methodClassClass").request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testInflectorMethodInstanceInterface() throws Exception {
+        final Response response = target("methodInstanceInterface").request()
+                .post(Entity.entity("value", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("value", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInflectorMethodInstanceInterfaceNegative() throws Exception {
+        final Response response = target("methodInstanceInterface").request()
+                .post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testInflectorMethodClassInterface() throws Exception {
+        final Response response = target("methodClassInterface").request()
+                .post(Entity.entity("value", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("value", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testInflectorMethodClassInterfaceNegative() throws Exception {
+        final Response response = target("methodClassInterface").request().post(Entity.entity(null, MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(500, response.getStatus());
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/PropertyValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/PropertyValidationTest.java
new file mode 100644
index 0000000..101ff5a
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/PropertyValidationTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import javax.validation.constraints.NotNull;
+
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.validation.ValidationFeature;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test various combinations of enabling/disabling: auto-discovery, bean validation, validation feature.
+ *
+ * @author Michal Gajdos
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class PropertyValidationTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        @NotNull
+        public String post(final String value) {
+            return value.isEmpty() ? null : value;
+        }
+    }
+
+    @Test
+    public void testDefaultValidationDefaultDiscoveryFeature() throws Exception {
+        _test(500, null, null, true);
+    }
+
+    @Test
+    public void testDefaultValidationDefaultDiscoveryNoFeature() throws Exception {
+        _test(500, null, null, false);
+    }
+
+    @Test
+    public void testDefaultValidationDiscoveryFeature() throws Exception {
+        _test(500, null, false, true);
+    }
+
+    @Test
+    public void testDefaultValidationDiscoveryNoFeature() throws Exception {
+        _test(500, null, false, false);
+    }
+
+    @Test
+    public void testDefaultValidationNoDiscoveryFeature() throws Exception {
+        _test(500, null, true, true);
+    }
+
+    @Test
+    public void testDefaultValidationNoDiscoveryNoFeature() throws Exception {
+        // Even though properties are disabled BV is registered.
+        _test(500, null, true, false);
+    }
+
+    @Test
+    public void testValidationDefaultDiscoveryFeature() throws Exception {
+        _test(500, false, null, true);
+    }
+
+    @Test
+    public void testValidationDefaultDiscoveryNoFeature() throws Exception {
+        _test(500, false, null, false);
+    }
+
+    @Test
+    public void testValidationDiscoveryFeature() throws Exception {
+        _test(500, false, false, true);
+    }
+
+    @Test
+    public void testValidationDiscoveryNoFeature() throws Exception {
+        _test(500, false, false, false);
+    }
+
+    @Test
+    public void testValidationNoDiscoveryFeature() throws Exception {
+        _test(500, false, true, true);
+    }
+
+    @Test
+    public void testValidationNoDiscoveryNoFeature() throws Exception {
+        // Even though properties are disabled BV is registered.
+        _test(500, false, true, false);
+    }
+
+    @Test
+    public void testNoValidationDefaultDiscoveryFeature() throws Exception {
+        _test(204, true, null, true);
+    }
+
+    @Test
+    public void testNoValidationDefaultDiscoveryNoFeature() throws Exception {
+        _test(204, true, null, false);
+    }
+
+    @Test
+    public void testNoValidationDiscoveryFeature() throws Exception {
+        _test(204, true, false, true);
+    }
+
+    @Test
+    public void testNoValidationDiscoveryNoFeature() throws Exception {
+        _test(204, true, false, false);
+    }
+
+    @Test
+    public void testNoValidationNoDiscoveryFeature() throws Exception {
+        _test(204, true, true, true);
+    }
+
+    @Test
+    public void testNoValidationNoDiscoveryNoFeature() throws Exception {
+        _test(204, true, true, false);
+    }
+
+    private void _test(final int responseStatus, final Boolean disableValidation,
+                       final Boolean disableAutoDiscovery, final boolean registerFeature) throws Exception {
+        final URI uri = URI.create("/");
+
+        assertApply(responseStatus, initResourceConfig(disableValidation, disableAutoDiscovery, registerFeature), uri);
+
+        if (responseStatus == 500) {
+            // validation works - environment is validation friendly -> let's try to disable META-INF/services lookup
+            final ResourceConfig resourceConfig = initResourceConfig(disableValidation, disableAutoDiscovery, true);
+            resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
+
+            assertApply(500, resourceConfig, uri);
+        }
+    }
+
+    private ResourceConfig initResourceConfig(final Boolean disableValidation,
+                                              final Boolean disableAutoDiscovery, final boolean registerFeature) {
+        final ResourceConfig resourceConfig = new ResourceConfig(Resource.class).register(LoggingFeature.class);
+
+        if (registerFeature) {
+            resourceConfig.register(ValidationFeature.class);
+        }
+        if (disableAutoDiscovery != null) {
+            resourceConfig.property(ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE, disableAutoDiscovery);
+        }
+        if (disableValidation != null) {
+            resourceConfig.property(ServerProperties.BV_FEATURE_DISABLE, disableValidation);
+        }
+        return resourceConfig;
+    }
+
+    private void assertApply(int responseStatus, ResourceConfig resourceConfig, URI uri)
+            throws InterruptedException, ExecutionException {
+        final ApplicationHandler applicationHandler = new ApplicationHandler(resourceConfig);
+        final ContainerRequest requestContext = new ContainerRequest(uri, uri, "POST", null, new MapPropertiesDelegate());
+        final ContainerResponse containerResponse = applicationHandler.apply(requestContext).get();
+
+        assertEquals(responseStatus, containerResponse.getStatus());
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ValidationInflector.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ValidationInflector.java
new file mode 100644
index 0000000..7f7755f
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ValidationInflector.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation;
+
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+
+import javax.validation.constraints.NotNull;
+
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.process.Inflector;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ValidationInflector implements Inflector<ContainerRequestContext, String> {
+
+    @NotNull
+    @Override
+    public String apply(final ContainerRequestContext requestContext) {
+        return get(requestContext);
+    }
+
+    @NotNull
+    public String get(@NotNull final ContainerRequestContext requestContext) {
+        try {
+            final String entity = ReaderWriter.readFromAsString(
+                    requestContext.getEntityStream(),
+                    requestContext.getMediaType());
+
+            return entity.isEmpty() ? null : entity;
+        } catch (IOException e) {
+            return "error";
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java
new file mode 100644
index 0000000..451b791
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation.validateonexecution;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.tests.e2e.server.validation.Extended;
+
+import org.hibernate.validator.constraints.Email;
+import org.hibernate.validator.constraints.NotBlank;
+
+/**
+ * @author Michal Gajdos
+ */
+@XmlRootElement
+public class AnotherContactBean implements Serializable {
+
+    private String email;
+
+    private String phone;
+
+    @NotBlank
+    private String name;
+
+    @NotNull(groups = {Extended.class})
+    private String city;
+
+    public AnotherContactBean() {
+    }
+
+    public AnotherContactBean(final String email, final String phone, final String name, final String city) {
+        this.email = email;
+        this.phone = phone;
+        this.name = name;
+        this.city = city;
+    }
+
+    @Email(regexp = "[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}")
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    @Pattern(regexp = "[0-9]{3,9}")
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(final String phone) {
+        this.phone = phone;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof AnotherContactBean)) {
+            return false;
+        }
+
+        final AnotherContactBean that = (AnotherContactBean) o;
+
+        if (email != null ? !email.equals(that.email) : that.email != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+        if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = email != null ? email.hashCode() : 0;
+        result = 31 * result + (phone != null ? phone.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "AnotherContactBean{"
+                + "email='" + email + '\''
+                + ", phone='" + phone + '\''
+                + ", name='" + name + '\''
+                + '}';
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionAbstractTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionAbstractTest.java
new file mode 100644
index 0000000..8ffb658
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionAbstractTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation.validateonexecution;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.util.runner.RunSeparately;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public abstract class ValidateOnExecutionAbstractTest extends JerseyTest {
+
+    @Test
+    public void testOnMethodValidateInputPassValidateExecutableDefault() throws Exception {
+        _testOnMethod("validateExecutableDefault", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateInputFailValidateExecutableDefault() throws Exception {
+        _testOnMethod("validateExecutableDefault", 15, 400);
+    }
+
+    @Test
+    public void testOnMethodValidateInputPassValidateExecutableMatch() throws Exception {
+        _testOnMethod("validateExecutableMatch", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateInputFailValidateExecutableMatch() throws Exception {
+        _testOnMethod("validateExecutableMatch", 15, 400);
+    }
+
+    @Test
+    public void testOnMethodValidateInputPassValidateExecutableMiss() throws Exception {
+        _testOnMethod("validateExecutableMiss", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateInputPassBiggerValidateExecutableMiss() throws Exception {
+        _testOnMethod("validateExecutableMiss", 15, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateInputPassValidateExecutableNone() throws Exception {
+        _testOnMethod("validateExecutableNone", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateInputPassBiggerValidateExecutableNone() throws Exception {
+        _testOnMethod("validateExecutableNone", 15, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateResultPassValidateExecutableDefault() throws Exception {
+        _testOnMethod("validateExecutableDefault", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateResultFailValidateExecutableDefault() throws Exception {
+        _testOnMethod("validateExecutableDefault", -15, 500);
+    }
+
+    @Test
+    public void testOnMethodValidateResultPassValidateExecutableMatch() throws Exception {
+        _testOnMethod("validateExecutableMatch", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateResultFailValidateExecutableMatch() throws Exception {
+        _testOnMethod("validateExecutableMatch", -15, 500);
+    }
+
+    @Test
+    public void testOnMethodValidateResultPassValidateExecutableMiss() throws Exception {
+        _testOnMethod("validateExecutableMiss", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateResultPassBiggerValidateExecutableMiss() throws Exception {
+        _testOnMethod("validateExecutableMiss", -15, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateResultPassValidateExecutableNone() throws Exception {
+        _testOnMethod("validateExecutableNone", 0, 200);
+    }
+
+    @Test
+    public void testOnMethodValidateResultPassBiggerValidateExecutableNone() throws Exception {
+        _testOnMethod("validateExecutableNone", -15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassValidateExecutableDefault() throws Exception {
+        _testOnType("default", 0, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassValidateExecutableMatch() throws Exception {
+        _testOnType("match", 0, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputFailValidateExecutableMatch() throws Exception {
+        _testOnType("match", 15, 400);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassValidateExecutableMiss() throws Exception {
+        _testOnType("miss", 0, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassBiggerValidateExecutableMiss() throws Exception {
+        _testOnType("miss", 15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassValidateExecutableNone() throws Exception {
+        _testOnType("none", 0, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassBiggerValidateExecutableNone() throws Exception {
+        _testOnType("none", 15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateResultPassValidateExecutableDefault() throws Exception {
+        _testOnType("default", 0, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateResultPassValidateExecutableMatch() throws Exception {
+        _testOnType("match", 0, 200);
+    }
+
+    @Test
+    @RunSeparately
+    public void testOnTypeValidateResultFailValidateExecutableMatch() throws Exception {
+        _testOnType("match", -15, 500);
+    }
+
+    @Test
+    public void testOnTypeValidateResultPassValidateExecutableMiss() throws Exception {
+        _testOnType("miss", 0, 200);
+    }
+
+    @Test
+    @RunSeparately
+    public void testOnTypeValidateResultPassBiggerValidateExecutableMiss() throws Exception {
+        _testOnType("miss", -15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateResultPassValidateExecutableNone() throws Exception {
+        _testOnType("none", 0, 200);
+    }
+
+    @Test
+    @RunSeparately
+    public void testOnTypeValidateResultPassBiggerValidateExecutableNone() throws Exception {
+        _testOnType("none", -15, 200);
+    }
+
+    @Test
+    public void testMixedValidatePassDefault() throws Exception {
+        _test("mixed-default", 0, 200);
+    }
+
+    @Test
+    public void testMixedValidateInputFailDefault() throws Exception {
+        _test("mixed-default", 15, 400);
+    }
+
+    @Test
+    public void testMixedValidateResultFailDefault() throws Exception {
+        _test("mixed-default", -15, 500);
+    }
+
+    @Test
+    public void testMixedValidatePassNone() throws Exception {
+        _test("mixed-none", 0, 200);
+    }
+
+    @Test
+    public void testMixedValidateInputPassNone() throws Exception {
+        _test("mixed-none", 15, 200);
+    }
+
+    @Test
+    public void testMixedValidateResultPassNone() throws Exception {
+        _test("mixed-none", -15, 200);
+    }
+
+    void _testOnMethod(final String path, final Integer value, final int returnStatus) throws Exception {
+        _test("on-method/" + path, value, returnStatus);
+    }
+
+    void _testOnType(final String path, final Integer value, final int returnStatus) throws Exception {
+        _test("on-type-" + path, value, returnStatus);
+    }
+
+    void _test(final String path, final Integer value, final int returnStatus) throws Exception {
+        final Response response = target(path)
+                .request()
+                .post(Entity.text(value));
+
+        assertEquals(returnStatus, response.getStatus());
+
+        if (returnStatus == 200) {
+            assertEquals(value, response.readEntity(Integer.class));
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java
new file mode 100644
index 0000000..da00461
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation.validateonexecution;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+import javax.validation.Valid;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.RunSeparately;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+// @RunWith(ConcurrentRunner.class)
+public class ValidateOnExecutionBasicTest extends ValidateOnExecutionAbstractTest {
+
+    /**
+     * On METHOD.
+     */
+
+    @Path("on-method")
+    public static class ValidateExecutableOnMethodsResource {
+
+        @POST
+        @Path("validateExecutableDefault")
+        @ValidateOnExecution
+        @Min(0)
+        public Integer validateExecutableDefault(@Max(10) final Integer value) {
+            return value;
+        }
+
+        @POST
+        @Path("validateExecutableMatch")
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        @Min(0)
+        public Integer validateExecutableMatch(@Max(10) final Integer value) {
+            return value;
+        }
+
+        @POST
+        @Path("validateExecutableMiss")
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        @Min(0)
+        public Integer validateExecutableMiss(@Max(10) final Integer value) {
+            return value;
+        }
+
+        @POST
+        @Path("validateExecutableNone")
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        @Min(0)
+        public Integer validateExecutableNone(@Max(10) final Integer value) {
+            return value;
+        }
+    }
+
+    /**
+     * On TYPE.
+     */
+
+    public abstract static class ValidateExecutableOnType {
+
+        @POST
+        @Min(0)
+        public Integer validateExecutable(@Max(10) final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-default")
+    @ValidateOnExecution
+    public static class ValidateExecutableOnTypeDefault extends ValidateExecutableOnType {
+    }
+
+    @Path("on-type-match")
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public static class ValidateExecutableOnTypeMatch extends ValidateExecutableOnType {
+    }
+
+    @Path("on-type-miss")
+    @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+    public static class ValidateExecutableOnTypeMiss extends ValidateExecutableOnType {
+    }
+
+    @Path("on-type-none")
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static class ValidateExecutableOnTypeNone extends ValidateExecutableOnType {
+    }
+
+    /**
+     * MIXED.
+     */
+
+    @Path("mixed-default")
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static class ValidateExecutableMixedDefault {
+
+        @POST
+        @Min(0)
+        @ValidateOnExecution
+        public Integer validateExecutable(@Max(10) final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("mixed-none")
+    @ValidateOnExecution
+    public static class ValidateExecutableMixedNone {
+
+        @POST
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public Integer validateExecutable(@Max(10) final Integer value) {
+            return value;
+        }
+    }
+
+    /**
+     * GETTERS.
+     */
+
+    public abstract static class ValidateGetterExecutable {
+
+        @GET
+        @Path("sanity")
+        public String getSanity() {
+            return "ok";
+        }
+    }
+
+    @Path("getter-on-method-default")
+    public static class ValidateGetterExecutableOnMethodDefault extends ValidateGetterExecutable {
+
+        @GET
+        @ValidateOnExecution
+        @NotNull
+        public String getDefault() {
+            return null;
+        }
+    }
+
+    @Path("getter-on-method-miss")
+    public static class ValidateGetterExecutableOnMethodMiss extends ValidateGetterExecutable {
+
+        @GET
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        @NotNull
+        public String getMiss() {
+            return null;
+        }
+    }
+
+    @Path("getter-on-method-match")
+    public static class ValidateGetterExecutableOnMethodMatch extends ValidateGetterExecutable {
+
+        @GET
+        @ValidateOnExecution(type = {ExecutableType.NON_GETTER_METHODS, ExecutableType.GETTER_METHODS})
+        @NotNull
+        public String getMatch() {
+            return null;
+        }
+    }
+
+    @Path("getter-on-type-default")
+    @ValidateOnExecution
+    public static class ValidateGetterExecutableOnTypeDefault extends ValidateGetterExecutable {
+
+        @GET
+        @NotNull
+        public String getDefault() {
+            return null;
+        }
+    }
+
+    @Path("getter-on-type-miss")
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public static class ValidateGetterExecutableOnTypeMiss extends ValidateGetterExecutable {
+
+        @GET
+        @NotNull
+        public String getMiss() {
+            return null;
+        }
+    }
+
+    @Path("getter-on-type-match")
+    @ValidateOnExecution(type = {ExecutableType.NON_GETTER_METHODS, ExecutableType.GETTER_METHODS})
+    public static class ValidateGetterExecutableOnTypeMatch extends ValidateGetterExecutable {
+
+        @GET
+        @NotNull
+        public String getMatch() {
+            return null;
+        }
+    }
+
+    /**
+     * BEANS.
+     */
+
+    @Path("getter-on-beans")
+    public static class ValidateGetterExecutableOnBeans {
+
+        @POST
+        @Valid
+        public AnotherContactBean post(@Valid final AnotherContactBean bean) {
+            return bean;
+        }
+
+        @POST
+        @Path("invalidMail")
+        @Valid
+        public AnotherContactBean invalidMail(@Valid final AnotherContactBean bean) {
+            bean.setEmail("ab");
+            return bean;
+        }
+
+        @POST
+        @Path("invalidPhone")
+        @Valid
+        public AnotherContactBean invalidPhone(@Valid final AnotherContactBean bean) {
+            bean.setPhone("12");
+            return bean;
+        }
+    }
+
+    @Path("getter-resource-method")
+    @Singleton
+    public static class ValidateGetterResourceMethod {
+
+        private int count = 1;
+
+        @GET
+        @Max(1)
+        public int getValue() {
+            return count++;
+        }
+    }
+
+    @Path("on-type-getter-null")
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public static class ValidateExecutableResource {
+
+        @Path("nogetter")
+        @GET
+        @NotNull
+        public String daNull() {
+            return null;
+        }
+
+        @Path("getter")
+        @GET
+        @NotNull
+        public String getNull() {
+            return null;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return new ResourceConfig(ValidateExecutableOnMethodsResource.class,
+                ValidateExecutableOnTypeDefault.class,
+                ValidateExecutableOnTypeMatch.class,
+                ValidateExecutableOnTypeMiss.class,
+                ValidateExecutableOnTypeNone.class,
+                ValidateExecutableMixedDefault.class,
+                ValidateExecutableMixedNone.class,
+                ValidateGetterExecutableOnMethodDefault.class,
+                ValidateGetterExecutableOnMethodMiss.class,
+                ValidateGetterExecutableOnMethodMatch.class,
+                ValidateGetterExecutableOnTypeDefault.class,
+                ValidateGetterExecutableOnTypeMiss.class,
+                ValidateGetterExecutableOnTypeMatch.class,
+                ValidateGetterExecutableOnBeans.class,
+                ValidateGetterResourceMethod.class,
+                ValidateExecutableResource.class)
+                .property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
+    }
+
+    @Test
+    public void testOnTypeValidateInputFailValidateExecutableDefault() throws Exception {
+        _testOnType("default", 15, 400);
+    }
+
+    @Test
+    @RunSeparately
+    public void testOnTypeValidateResultFailValidateExecutableDefault() throws Exception {
+        _testOnType("default", -15, 500);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassBiggerValidateExecutableMiss() throws Exception {
+        _testOnType("miss", 15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassBiggerValidateExecutableNone() throws Exception {
+        _testOnType("none", 15, 200);
+    }
+
+    @Test
+    public void testOnMethodGetterDefault() throws Exception {
+        final WebTarget target = target("getter-on-method-default");
+
+        assertThat(target.request().get().getStatus(), equalTo(400));
+        assertThat(target.path("sanity").request().get().getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testOnMethodGetterMiss() throws Exception {
+        final WebTarget target = target("getter-on-method-miss");
+
+        Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(400));
+
+        response = target.path("sanity").request().get();
+        assertThat(response.getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testOnMethodGetterMatch() throws Exception {
+        final WebTarget target = target("getter-on-method-match");
+
+        assertThat(target.request().get().getStatus(), equalTo(400));
+        assertThat(target.path("sanity").request().get().getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testOnTypeGetterDefault() throws Exception {
+        final WebTarget target = target("getter-on-type-default");
+
+        assertThat(target.request().get().getStatus(), equalTo(400));
+        assertThat(target.path("sanity").request().get().getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testOnTypeGetterMiss() throws Exception {
+        final WebTarget target = target("getter-on-type-miss");
+
+        Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(400));
+
+        response = target.path("sanity").request().get();
+        assertThat(response.getStatus(), equalTo(400));
+    }
+
+    /**
+     * Validation should fail when getter (also a resource method) is invoked and not when the resource class is validated.
+     */
+    @Test
+    public void testGetterResourceMethod() throws Exception {
+        final WebTarget target = target("getter-resource-method");
+        final Response response = target.request().get();
+
+        assertThat(response.getStatus(), equalTo(500));
+    }
+
+    @Test
+    public void testOnTypeGetterNull() throws Exception {
+        final WebTarget target = target("on-type-getter-null");
+
+        Response response = target.path("nogetter").request().get();
+        assertThat(response.getStatus(), equalTo(400));
+
+        response = target.path("getter").request().get();
+        assertThat(response.getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testOnTypeGetterMatch() throws Exception {
+        final WebTarget target = target("getter-on-type-match");
+
+        assertThat(target.request().get().getStatus(), equalTo(400));
+        assertThat(target.path("sanity").request().get().getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testBeansPositive() throws Exception {
+        final WebTarget target = target("getter-on-beans");
+        final AnotherContactBean contactBean = new AnotherContactBean("jersey@example.com", null, "Jersey JAX-RS", null);
+
+        final Response response = target.request().post(Entity.xml(contactBean));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(AnotherContactBean.class), equalTo(contactBean));
+    }
+
+    @Test
+    public void testBeansValidateGetterInvalidEmail() throws Exception {
+        final WebTarget target = target("getter-on-beans");
+        final AnotherContactBean contactBean = new AnotherContactBean("jersey", null, "Jersey JAX-RS", null);
+
+        final Response response = target.request().post(Entity.xml(contactBean));
+
+        assertThat(response.getStatus(), equalTo(400));
+    }
+
+    @Test
+    public void testBeansValidateGetterInvalidPhone() throws Exception {
+        final WebTarget target = target("getter-on-beans");
+        final AnotherContactBean contactBean = new AnotherContactBean("jersey@example.com", "12", "Jersey JAX-RS", null);
+
+        final Response response = target.request().post(Entity.xml(contactBean));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(AnotherContactBean.class), equalTo(contactBean));
+    }
+
+    @Test
+    public void testBeansValidateGetterInvalidReturnMail() throws Exception {
+        final WebTarget target = target("getter-on-beans").path("invalidMail");
+        final AnotherContactBean contactBean = new AnotherContactBean("jersey@example.com", null, "Jersey JAX-RS", null);
+
+        final Response response = target.request().post(Entity.xml(contactBean));
+
+        assertThat(response.getStatus(), equalTo(500));
+    }
+
+    @Test
+    public void testBeansValidateGetterInvalidReturnPhone() throws Exception {
+        final WebTarget target = target("getter-on-beans").path("invalidPhone");
+        final AnotherContactBean contactBean = new AnotherContactBean("jersey@example.com", null, "Jersey JAX-RS", null);
+
+        final Response response = target.request().post(Entity.xml(contactBean));
+        contactBean.setPhone("12");
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(AnotherContactBean.class), equalTo(contactBean));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceGenericsTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceGenericsTest.java
new file mode 100644
index 0000000..662bfd4
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceGenericsTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation.validateonexecution;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * @author Michal Gajdos
+ */
+// @RunWith(ConcurrentRunner.class)
+public class ValidateOnExecutionInheritanceGenericsTest extends ValidateOnExecutionAbstractTest {
+
+    /**
+     * On METHOD.
+     */
+
+    /**
+     * {@link ValidateOnExecution} annotations from this interface should be considered during validating phase.
+     */
+    @SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+    public static interface ValidateExecutableOnMethodsValidation<T extends Number> {
+
+        @Min(0)
+        @ValidateOnExecution
+        public T validateExecutableDefault(@Max(10) final T value);
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        public T validateExecutableMatch(@Max(10) final T value);
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public T validateExecutableMiss(@Max(10) final T value);
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public T validateExecutableNone(@Max(10) final T value);
+    }
+
+    /**
+     * Wrong generic types. {@link ValidateOnExecution} annotations should not be considered at all.
+     *
+     * @param <T>
+     */
+    @SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+    public static interface ValidateExecutableOnMethodsCharSequenceValidation<T extends CharSequence> {
+
+        @Min(10)
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public T validateExecutableDefault(@Max(0) final T value);
+
+        @Min(10)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public T validateExecutableMatch(@Max(0) final T value);
+
+        @Min(10)
+        @ValidateOnExecution
+        public T validateExecutableMiss(@Max(0) final T value);
+
+        @Min(10)
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        public T validateExecutableNone(@Max(0) final T value);
+    }
+
+    @ValidateOnExecution(type = ExecutableType.ALL)
+    public static interface ValidateExecutableOnMethodsJaxRs extends ValidateExecutableOnMethodsValidation<Integer> {
+
+        @POST
+        @Path("validateExecutableDefault")
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        Integer validateExecutableDefault(final Integer value);
+
+        @POST
+        @Path("validateExecutableMatch")
+        @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+        Integer validateExecutableMatch(final Integer value);
+
+        @POST
+        @Path("validateExecutableMiss")
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        Integer validateExecutableMiss(final Integer value);
+
+        @POST
+        @Path("validateExecutableNone")
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        Integer validateExecutableNone(final Integer value);
+    }
+
+    public abstract static class ValidateExecutableOnMethodsAbstractResource
+            implements ValidateExecutableOnMethodsJaxRs, ValidateExecutableOnMethodsCharSequenceValidation<String> {
+
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public abstract Integer validateExecutableDefault(final Integer value);
+
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public abstract Integer validateExecutableMatch(final Integer value);
+
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        public abstract Integer validateExecutableMiss(final Integer value);
+
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        public abstract Integer validateExecutableNone(final Integer value);
+    }
+
+    @Path("on-method")
+    public static class ValidateExecutableOnMethodsResource extends ValidateExecutableOnMethodsAbstractResource {
+
+        public Integer validateExecutableDefault(final Integer value) {
+            return value;
+        }
+
+        public Integer validateExecutableMatch(final Integer value) {
+            return value;
+        }
+
+        public Integer validateExecutableMiss(final Integer value) {
+            return value;
+        }
+
+        public Integer validateExecutableNone(final Integer value) {
+            return value;
+        }
+
+        public String validateExecutableDefault(final String value) {
+            return value;
+        }
+
+        public String validateExecutableMatch(final String value) {
+            return value;
+        }
+
+        public String validateExecutableMiss(final String value) {
+            return value;
+        }
+
+        public String validateExecutableNone(final String value) {
+            return value;
+        }
+    }
+
+    /**
+     * On TYPE.
+     */
+
+    @SuppressWarnings("JavaDoc")
+    public static interface ValidateExecutableOnType<T extends Number> {
+
+        @Min(0)
+        public T validateExecutable(@Max(10) final T value);
+    }
+
+    @SuppressWarnings("JavaDoc")
+    public static interface ValidateExecutableCharSequenceOnType<X extends CharSequence> {
+
+        @Min(10)
+        public X validateExecutable(@Max(0) final X value);
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableOnTypeDefault extends ValidateExecutableOnType<Integer> {
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableCharSequenceOnTypeDefault extends ValidateExecutableCharSequenceOnType<String> {
+
+        @ValidateOnExecution
+        public String validateExecutable(final String value);
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+    public abstract static class ValidateExecutableOnTypeDefaultAbstractResource implements ValidateExecutableOnTypeDefault {
+
+        @POST
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-default")
+    @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+    public static class ValidateExecutableOnTypeDefaultResource extends ValidateExecutableOnTypeDefaultAbstractResource
+            implements ValidateExecutableCharSequenceOnTypeDefault {
+
+        @POST
+        @Path("another")
+        public String validateExecutable(final String value) {
+            return value;
+        }
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public static interface ValidateExecutableOnTypeMatch extends ValidateExecutableOnType<Integer> {
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableCharSequenceOnTypeMatch extends ValidateExecutableCharSequenceOnType<String> {
+
+        @ValidateOnExecution
+        public String validateExecutable(final String value);
+    }
+
+    @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+    public abstract static class ValidateExecutableOnTypeMatchAbstractResource implements ValidateExecutableOnTypeMatch {
+
+        @POST
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-match")
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static class ValidateExecutableOnTypeMatchResource extends ValidateExecutableOnTypeMatchAbstractResource
+            implements ValidateExecutableCharSequenceOnTypeMatch {
+
+        @POST
+        @Path("another")
+        public String validateExecutable(final String value) {
+            return value;
+        }
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+    public static interface ValidateExecutableOnTypeMiss extends ValidateExecutableOnType<Integer> {
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableCharSequenceOnTypeMiss extends ValidateExecutableCharSequenceOnType<String> {
+
+        @ValidateOnExecution
+        public String validateExecutable(final String value);
+    }
+
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public abstract static class ValidateExecutableOnTypeMissAbstractResource implements ValidateExecutableOnTypeMiss {
+
+        @POST
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-miss")
+    @ValidateOnExecution
+    public static class ValidateExecutableOnTypeMissResource extends ValidateExecutableOnTypeMissAbstractResource
+            implements ValidateExecutableCharSequenceOnTypeMiss {
+
+        @POST
+        @Path("another")
+        public String validateExecutable(final String value) {
+            return value;
+        }
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static interface ValidateExecutableOnTypeNone extends ValidateExecutableOnType<Integer> {
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableCharSequenceOnTypeNone extends ValidateExecutableCharSequenceOnType<String> {
+
+        @ValidateOnExecution
+        public String validateExecutable(final String value);
+    }
+
+    @ValidateOnExecution(type = ExecutableType.ALL)
+    public abstract static class ValidateExecutableOnTypeNoneAbstractResource implements ValidateExecutableOnTypeNone {
+
+        @POST
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-none")
+    @ValidateOnExecution(type = {ExecutableType.CONSTRUCTORS, ExecutableType.NON_GETTER_METHODS})
+    public static class ValidateExecutableOnTypeNoneResource extends ValidateExecutableOnTypeNoneAbstractResource
+            implements ValidateExecutableCharSequenceOnTypeNone {
+
+        @POST
+        @Path("another")
+        public String validateExecutable(final String value) {
+            return value;
+        }
+    }
+
+    /**
+     * MIXED.
+     */
+
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static interface ValidateExecutableMixedDefault<T extends Number> {
+
+        @Min(0)
+        @ValidateOnExecution
+        public T validateExecutable(@Max(10) final T value);
+    }
+
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static interface ValidateExecutableCharSequenceMixedDefault<T extends CharSequence> {
+
+        @Min(10)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public T validateExecutable(@Max(0) final T value);
+    }
+
+    @Path("mixed-default")
+    public static class ValidateExecutableMixedDefaultResource
+            implements ValidateExecutableMixedDefault<Integer>, ValidateExecutableCharSequenceMixedDefault<String> {
+
+        @POST
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+
+        @POST
+        @Path("another")
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public String validateExecutable(final String value) {
+            return value;
+        }
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableMixedNone<T extends Number> {
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public T validateExecutable(@Max(10) final T value);
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableCharSequenceMixedNone<T extends CharSequence> {
+
+        @Min(10)
+        @ValidateOnExecution
+        public T validateExecutable(@Max(0) final T value);
+    }
+
+    @Path("mixed-none")
+    public static class ValidateExecutableMixedNoneResource
+            implements ValidateExecutableMixedNone<Integer>, ValidateExecutableCharSequenceMixedNone<String> {
+
+        @POST
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+
+        @POST
+        @Path("another")
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        public String validateExecutable(final String value) {
+            return value;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ValidateExecutableOnMethodsResource.class,
+                ValidateExecutableOnTypeDefaultResource.class,
+                ValidateExecutableOnTypeMatchResource.class,
+                ValidateExecutableOnTypeMissResource.class,
+                ValidateExecutableOnTypeNoneResource.class,
+                ValidateExecutableMixedDefaultResource.class,
+                ValidateExecutableMixedNoneResource.class)
+                .property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassValidateExecutableDefault() throws Exception {
+        _testOnType("default", 15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateResultPassNoValidateExecutableDefault() throws Exception {
+        _testOnType("default", -15, 200);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceTest.java
new file mode 100644
index 0000000..b606a66
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation.validateonexecution;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+import org.junit.Test;
+
+/**
+ * @author Michal Gajdos
+ */
+// @RunWith(ConcurrentRunner.class)
+public class ValidateOnExecutionInheritanceTest extends ValidateOnExecutionAbstractTest {
+
+    /**
+     * On METHOD.
+     */
+
+    /**
+     * {@link ValidateOnExecution} annotations from this interface should be considered during validating phase.
+     */
+    @SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+    public static interface ValidateExecutableOnMethodsValidation {
+
+        @Min(0)
+        @ValidateOnExecution
+        public Integer validateExecutableDefault(@Max(10) final Integer value);
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        public Integer validateExecutableMatch(@Max(10) final Integer value);
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public Integer validateExecutableMiss(@Max(10) final Integer value);
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public Integer validateExecutableNone(@Max(10) final Integer value);
+    }
+
+    @ValidateOnExecution(type = ExecutableType.ALL)
+    public static interface ValidateExecutableOnMethodsJaxRs extends ValidateExecutableOnMethodsValidation {
+
+        @POST
+        @Path("validateExecutableDefault")
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        Integer validateExecutableDefault(final Integer value);
+
+        @POST
+        @Path("validateExecutableMatch")
+        @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+        Integer validateExecutableMatch(final Integer value);
+
+        @POST
+        @Path("validateExecutableMiss")
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        Integer validateExecutableMiss(final Integer value);
+
+        @POST
+        @Path("validateExecutableNone")
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        Integer validateExecutableNone(final Integer value);
+    }
+
+    public abstract static class ValidateExecutableOnMethodsAbstractResource implements ValidateExecutableOnMethodsJaxRs {
+
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public abstract Integer validateExecutableDefault(final Integer value);
+
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public abstract Integer validateExecutableMatch(final Integer value);
+
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        public abstract Integer validateExecutableMiss(final Integer value);
+
+        @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+        public abstract Integer validateExecutableNone(final Integer value);
+    }
+
+    @Path("on-method")
+    public static class ValidateExecutableOnMethodsResource extends ValidateExecutableOnMethodsAbstractResource {
+
+        public Integer validateExecutableDefault(final Integer value) {
+            return value;
+        }
+
+        public Integer validateExecutableMatch(final Integer value) {
+            return value;
+        }
+
+        public Integer validateExecutableMiss(final Integer value) {
+            return value;
+        }
+
+        public Integer validateExecutableNone(final Integer value) {
+            return value;
+        }
+    }
+
+    /**
+     * On TYPE.
+     */
+
+    @SuppressWarnings("JavaDoc")
+    public static interface ValidateExecutableOnType {
+
+        @POST
+        @Min(0)
+        public Integer validateExecutable(@Max(10) final Integer value);
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableOnTypeDefault extends ValidateExecutableOnType {
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+    public abstract static class ValidateExecutableOnTypeDefaultAbstractResource implements ValidateExecutableOnTypeDefault {
+
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-default")
+    @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+    public static class ValidateExecutableOnTypeDefaultResource extends ValidateExecutableOnTypeDefaultAbstractResource {
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public static interface ValidateExecutableOnTypeMatch extends ValidateExecutableOnType {
+    }
+
+    @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+    public abstract static class ValidateExecutableOnTypeMatchAbstractResource implements ValidateExecutableOnTypeMatch {
+
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-match")
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static class ValidateExecutableOnTypeMatchResource extends ValidateExecutableOnTypeMatchAbstractResource {
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+    public static interface ValidateExecutableOnTypeMiss extends ValidateExecutableOnType {
+    }
+
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public abstract static class ValidateExecutableOnTypeMissAbstractResource implements ValidateExecutableOnTypeMiss {
+
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-miss")
+    @ValidateOnExecution
+    public static class ValidateExecutableOnTypeMissResource extends ValidateExecutableOnTypeMissAbstractResource {
+    }
+
+    /**
+     * This {@link ValidateOnExecution} annotation should be considered during validating phase.
+     */
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static interface ValidateExecutableOnTypeNone extends ValidateExecutableOnType {
+    }
+
+    @ValidateOnExecution(type = ExecutableType.ALL)
+    public abstract static class ValidateExecutableOnTypeNoneAbstractResource implements ValidateExecutableOnTypeNone {
+
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Path("on-type-none")
+    @ValidateOnExecution(type = {ExecutableType.CONSTRUCTORS, ExecutableType.NON_GETTER_METHODS})
+    public static class ValidateExecutableOnTypeNoneResource extends ValidateExecutableOnTypeNoneAbstractResource {
+    }
+
+    /**
+     * MIXED.
+     */
+
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static interface ValidateExecutableMixedDefault {
+
+        @Min(0)
+        @ValidateOnExecution
+        public Integer validateExecutable(@Max(10) final Integer value);
+    }
+
+    @Path("mixed-default")
+    public static class ValidateExecutableMixedDefaultResource implements ValidateExecutableMixedDefault {
+
+        @POST
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableMixedNone {
+
+        @Min(0)
+        @ValidateOnExecution(type = ExecutableType.NONE)
+        public Integer validateExecutable(@Max(10) final Integer value);
+    }
+
+    @Path("mixed-none")
+    public static class ValidateExecutableMixedNoneResource implements ValidateExecutableMixedNone {
+
+        @POST
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @ValidateOnExecution
+    public static interface ValidateExecutableMixedClassDefault {
+
+        @Min(0)
+        public Integer validateExecutable(@Max(10) final Integer value);
+    }
+
+    @Path("mixed-class-default")
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static class ValidateExecutableMixedClassDefaultResource implements ValidateExecutableMixedClassDefault {
+
+        @POST
+        @ValidateOnExecution(type = ExecutableType.CONSTRUCTORS)
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @ValidateOnExecution(type = ExecutableType.NONE)
+    public static interface ValidateExecutableMixedClassNone {
+
+        @Min(0)
+        public Integer validateExecutable(@Max(10) final Integer value);
+    }
+
+    @Path("mixed-class-none")
+    @ValidateOnExecution(type = ExecutableType.NON_GETTER_METHODS)
+    public static class ValidateExecutableMixedClassNoneResource implements ValidateExecutableMixedClassNone {
+
+        @POST
+        @ValidateOnExecution(type = ExecutableType.ALL)
+        public Integer validateExecutable(final Integer value) {
+            return value;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ValidateExecutableOnMethodsResource.class,
+                ValidateExecutableOnTypeNoneResource.class,
+                ValidateExecutableOnTypeMissResource.class,
+                ValidateExecutableOnTypeMatchResource.class,
+                ValidateExecutableOnTypeDefaultResource.class,
+                ValidateExecutableMixedDefaultResource.class,
+                ValidateExecutableMixedNoneResource.class,
+                ValidateExecutableMixedClassNoneResource.class,
+                ValidateExecutableMixedClassDefaultResource.class)
+                .property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
+    }
+
+    @Test
+    public void testOnTypeValidateInputPassValidateExecutableDefault() throws Exception {
+        _testOnType("default", 15, 200);
+    }
+
+    @Test
+    public void testOnTypeValidateResultPassNoValidateExecutableDefault() throws Exception {
+        _testOnType("default", -15, 200);
+    }
+
+    @Test
+    public void testMixedClassValidatePassDefault() throws Exception {
+        _test("mixed-class-default", 0, 200);
+    }
+
+    @Test
+    public void testMixedClassValidateInputPassValidateDefault() throws Exception {
+        _test("mixed-class-default", 15, 200);
+    }
+
+    @Test
+    public void testMixedClassValidateResultPassNoValidateDefault() throws Exception {
+        _test("mixed-class-default", -15, 200);
+    }
+
+    @Test
+    public void testMixedClassValidatePassNone() throws Exception {
+        _test("mixed-class-none", 0, 200);
+    }
+
+    @Test
+    public void testMixedClassValidateInputPassNone() throws Exception {
+        _test("mixed-class-none", 15, 200);
+    }
+
+    @Test
+    public void testMixedClassValidateResultPassNone() throws Exception {
+        _test("mixed-class-none", -15, 200);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionOverrideTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionOverrideTest.java
new file mode 100644
index 0000000..6c54c42
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionOverrideTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013, 2018 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.validation.validateonexecution;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import javax.validation.ValidationException;
+import javax.validation.constraints.NotNull;
+import javax.validation.executable.ExecutableType;
+import javax.validation.executable.ValidateOnExecution;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Testing whether an {@link javax.validation.ValidationException} is raised when {@link ValidateOnExecution} is present on
+ * overriding/implementing method as well.
+ *
+ * @author Michal Gajdos
+ */
+public class ValidateOnExecutionOverrideTest extends JerseyTest {
+
+    public static interface Validation {
+
+        @NotNull
+        @ValidateOnExecution
+        public String interfaceMessage();
+    }
+
+    public abstract static class ValidationBase {
+
+        @NotNull
+        @ValidateOnExecution
+        public abstract String classMessage();
+    }
+
+    @Path("/")
+    public static class ValidationResource extends ValidationBase implements Validation {
+
+        @GET
+        @Path("interface")
+        @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+        public String interfaceMessage() {
+            return "ko";
+        }
+
+        @GET
+        @Path("class")
+        @ValidateOnExecution(type = ExecutableType.GETTER_METHODS)
+        public String classMessage() {
+            return "ko";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+
+        return new ResourceConfig(ValidationResource.class);
+    }
+
+    @Test
+    public void testOverridingCheckOnInterface() throws Exception {
+        _test("interface");
+    }
+
+    @Test
+    public void testOverridingCheckOnClass() throws Exception {
+        _test("class");
+    }
+
+    private void _test(final String path) throws Exception {
+        assertThat(target(path).request().get().getStatus(), equalTo(500));
+
+        final List<LogRecord> loggedRecords = getLoggedRecords();
+        assertThat(loggedRecords.size(), equalTo(1));
+        assertThat(loggedRecords.get(0).getThrown(), instanceOf(ValidationException.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/OverrideWadlResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/OverrideWadlResourceTest.java
new file mode 100644
index 0000000..4b38262
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/OverrideWadlResourceTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2018 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.wadl;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author Paul Sandoz
+ */
+public class OverrideWadlResourceTest extends JerseyTest {
+
+    // The explicit @Produces definition improves the compatibility of the test case with other connectors.
+    // Unlike HttpUrlConnector, others might not be sending default accepted media types ((namely the JdkConnector)
+    // and the resource would not be matched.
+    @Produces("text/plain")
+    @Path("application.wadl")
+    public static class OverrideWadlApplicationResource {
+        @GET
+        public String get() {
+            return "OVERRIDE";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(OverrideWadlApplicationResource.class);
+    }
+
+    @Test
+    public void testOverride() {
+        WebTarget target = target("/application.wadl");
+
+        assertEquals("OVERRIDE", target.request().get(String.class));
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/ResourceExtendedFlagTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/ResourceExtendedFlagTest.java
new file mode 100644
index 0000000..3da5917
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/ResourceExtendedFlagTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2013, 2018 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.wadl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.internal.util.SaxHelper;
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ExtendedResourceContext;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.ExtendedResource;
+import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.server.model.ResourceMethod;
+import org.glassfish.jersey.server.model.ResourceModel;
+import org.glassfish.jersey.server.wadl.internal.WadlUtils;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test verifies functionality of {@link org.glassfish.jersey.server.model.Resource#isExtended()} and its
+ * influence on WADL.
+ *
+ * @author Miroslav Fuksa
+ */
+public class ResourceExtendedFlagTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class, AllExtended.class, LoggingFeature.class);
+    }
+
+    @Path("all-extended")
+    public static class AllExtended {
+
+        @GET
+        @ExtendedResource
+        public String allExtendedGet() {
+            return "get";
+        }
+
+        @GET
+        @Path("sub")
+        @ExtendedResource
+        public String allExtendedSubGet() {
+            return "sub-get";
+        }
+
+        @Path("locator")
+        @ExtendedResource
+        public SubResourceLocator allExtendedSubLocator() {
+            return new SubResourceLocator();
+        }
+    }
+
+    public static class SubResourceLocator {
+
+        @GET
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Path("resource")
+    public static class MyResource {
+
+        @Context
+        private ExtendedResourceContext extendedResourceContext;
+
+        @GET
+        public String resourceGet() {
+
+            final String error = validateModel();
+            return error == null ? "ok" : error;
+        }
+
+        @GET
+        @Path("extended")
+        @ExtendedResource
+        public String resourceExtendedGet() {
+            return "extended";
+        }
+
+        @POST
+        @ExtendedResource
+        public String resourceExtendedPost() {
+            return "extendedpost";
+        }
+
+        @POST
+        @Path("visible")
+        public String resourceVisiblePost() {
+            return "visiblepost";
+        }
+
+        private String validateModel() {
+            Map<String, Boolean> extendedMethods = new HashMap<>();
+            extendedMethods.put("resourceExtendedGet", false);
+            extendedMethods.put("resourceExtendedPost", false);
+            extendedMethods.put("allExtendedGet", false);
+            extendedMethods.put("allExtendedSubGet", false);
+            extendedMethods.put("allExtendedSubLocator", false);
+
+            Map<String, Boolean> visibleMethods = new HashMap<>();
+            visibleMethods.put("resourceVisiblePost", false);
+            visibleMethods.put("resourceGet", false);
+
+            final ResourceModel resourceModel = extendedResourceContext.getResourceModel();
+            for (Resource rootResource : resourceModel.getRootResources()) {
+                final String error = checkResource(rootResource, extendedMethods, visibleMethods, "");
+                if (error != null) {
+                    return error;
+                }
+            }
+
+            for (Map.Entry<String, Boolean> entry : extendedMethods.entrySet()) {
+                if (entry.getValue() != null && !entry.getValue()) {
+                    return "Extended method " + entry.getKey() + " was not found";
+                }
+            }
+
+            for (Map.Entry<String, Boolean> entry : visibleMethods.entrySet()) {
+                if (entry.getValue() != null && !entry.getValue()) {
+                    return "Visible method " + entry.getKey() + " was not found";
+                }
+            }
+            return null;
+        }
+
+        private String checkResource(Resource resource, Map<String, Boolean> extendedMethods,
+                                     Map<String, Boolean> visibleMethods, String prefix) {
+
+            System.out.println(prefix + "R extended=" + resource.isExtended() + " resource: " + resource);
+
+            boolean allExtended = true;
+            for (ResourceMethod resourceMethod : resource.getAllMethods()) {
+                if ("OPTIONS".equals(resourceMethod.getHttpMethod()) && !resourceMethod.isExtended()) {
+                    return "OPTIONS method " + resourceMethod + " is not extended";
+                }
+                if (!resourceMethod.isExtended()) {
+                    allExtended = false;
+                }
+
+                final String methodName = resourceMethod.getInvocable().getHandlingMethod().getName();
+                if (extendedMethods.get(methodName) != null) {
+                    extendedMethods.put(methodName, true);
+                    if (!resourceMethod.isExtended()) {
+                        return "Method " + methodName + " should be extended.";
+                    }
+                }
+
+                if (visibleMethods.get(methodName) != null) {
+                    visibleMethods.put(methodName, true);
+                    if (resourceMethod.isExtended()) {
+                        return "Method " + methodName + " should NOT be extended.";
+                    }
+                }
+                System.out.println(prefix + "   M extended=" + resourceMethod.isExtended() + " method: " + methodName);
+            }
+
+            for (Resource child : resource.getChildResources()) {
+                final String error = checkResource(child, extendedMethods, visibleMethods, "     ");
+                if (error != null) {
+                    return error;
+                }
+            }
+
+            if (allExtended != resource.isExtended()) {
+                return "Resource " + resource + "Resource.extended = " + resource.isExtended() + " and allExtended="
+                        + allExtended;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Invokes resource method which goes trough the {@link org.glassfish.jersey.server.model.ResourceModel}
+     * and checks whether {@code extended} flag is correctly defined on resources and methods.
+     */
+    @Test
+    public void testResourceModel() {
+        final Response response = target().path("resource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("ok", response.readEntity(String.class));
+    }
+
+    /**
+     * Tests full wadl with all "extended" details.
+     *
+     * @throws ParserConfigurationException
+     * @throws XPathExpressionException
+     * @throws IOException
+     * @throws SAXException
+     */
+    @Test
+    public void testDetailedWadl() throws ParserConfigurationException, XPathExpressionException, IOException,
+            SAXException {
+        Response response = target("/application.wadl").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                .request(MediaTypes.WADL_TYPE).get();
+        assertEquals(200, response.getStatus());
+        File tmpFile = response.readEntity(File.class);
+        DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+        bf.setNamespaceAware(true);
+        bf.setValidating(false);
+        if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+            bf.setXIncludeAware(false);
+        }
+        DocumentBuilder b = bf.newDocumentBuilder();
+        Document d = b.parse(tmpFile);
+        printSource(new DOMSource(d));
+        XPath xp = XPathFactory.newInstance().newXPath();
+        xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+        // check base URI
+        String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+        assertEquals(val, getBaseUri().toString());
+        // check total number of resources is 8
+        val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+        assertEquals("8", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='all-extended'])", d, XPathConstants.STRING);
+        assertEquals("1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='resource'])", d, XPathConstants.STRING);
+        assertEquals("1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='application.wadl'])", d, XPathConstants.STRING);
+        assertEquals("1", val);
+
+        xp.setNamespaceContext(new SimpleNamespaceResolver("jersey", "http://jersey.java.net/"));
+        val = (String) xp.evaluate("count(//jersey:extended)", d, XPathConstants.STRING);
+        assertEquals("31", val);
+    }
+
+    /**
+     * Tests limited wadl with only user resources.
+     *
+     * @throws ParserConfigurationException
+     * @throws XPathExpressionException
+     * @throws IOException
+     * @throws SAXException
+     */
+    @Test
+    public void testLimitedWadl() throws ParserConfigurationException, XPathExpressionException, IOException,
+            SAXException {
+        Response response = target("/application.wadl").request(MediaTypes.WADL_TYPE).get();
+        assertEquals(200, response.getStatus());
+        File tmpFile = response.readEntity(File.class);
+        DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+        bf.setNamespaceAware(true);
+        bf.setValidating(false);
+        if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+            bf.setXIncludeAware(false);
+        }
+        DocumentBuilder b = bf.newDocumentBuilder();
+        Document d = b.parse(tmpFile);
+        printSource(new DOMSource(d));
+        XPath xp = XPathFactory.newInstance().newXPath();
+        xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+        // check base URI
+        String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+        assertEquals(val, getBaseUri().toString());
+        // check total number of resources is 8
+        val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+        assertEquals("2", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='all-extended'])", d, XPathConstants.STRING);
+        assertEquals("0", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='resource'])", d, XPathConstants.STRING);
+        assertEquals("1", val);
+
+        val = (String) xp.evaluate("count(//wadl:resource[@path='application.wadl'])", d, XPathConstants.STRING);
+        assertEquals("0", val);
+
+        xp.setNamespaceContext(new SimpleNamespaceResolver("jersey", "http://jersey.java.net/"));
+        val = (String) xp.evaluate("count(//jersey:extended)", d, XPathConstants.STRING);
+        assertEquals("0", val);
+    }
+
+    public static void printSource(Source source) {
+        try {
+            System.out.println("---------------------");
+            Transformer trans = TransformerFactory.newInstance().newTransformer();
+            Properties oprops = new Properties();
+            oprops.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
+            oprops.put(OutputKeys.INDENT, "yes");
+            oprops.put(OutputKeys.METHOD, "xml");
+            trans.setOutputProperties(oprops);
+            trans.transform(source, new StreamResult(System.out));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java
new file mode 100644
index 0000000..d2df87e
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2015, 2018 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.wadl;
+
+import java.io.StringWriter;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.ElementNameAndTextQualifier;
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Tests whether WADL for a {@link BeanParam} annotated resource method parameter is generated properly.
+ * <p/>
+ * The tests of this class perform a comparison of
+ * <pre><ul>
+ *     <li>a WADL of a reference resource that has {@code *Param} annotated class fields or resource method parameters</li>
+ *     <li>with a resource configured with {@link BeanParam} annotated parameters where some of the reference resource parameters
+ *          are aggregated in the class that is used as a bean param</li>
+ * </ul></pre>
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class WadlBeanParamTest extends JerseyTest {
+
+    private final ElementNameAndTextQualifier elementQualifier = new ElementNameAndTextQualifier() {
+
+        /**
+         * For {@code <param ??? />} nodes, the comparison is based on matching {@code name} attributes while ignoring
+         * their order. For any other nodes, strict comparison (including ordering) is made.
+         *
+         * @param control The reference element to compare the {@code test} with.
+         * @param test The test element to compare against {@code control}.
+         * @return Whether given nodes qualify for comparison.
+         */
+        @Override
+        public boolean qualifyForComparison(final Element control, final Element test) {
+            if (test != null && !"param".equals(test.getNodeName())) {
+                return super.qualifyForComparison(control, test);
+            }
+            if (!(control != null && test != null
+                          && equalsNamespace(control, test)
+                          && getNonNamespacedNodeName(control).equals(getNonNamespacedNodeName(test)))) {
+                return false;
+            }
+            if (control.hasAttribute("name") && test.hasAttribute("name")) {
+                if (control.getAttribute("name").equals(test.getAttribute("name"))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ReferenceResourceBeanParam.class, TestResourceBeanParam.class,
+                TestResourceConstructorInitializedBeanParam.class, TestResourceFieldBeanParam.class);
+    }
+
+    private String nodeAsString(final Object resourceNode) throws TransformerException {
+        StringWriter writer = new StringWriter();
+        Transformer transformer = TransformerFactory.newInstance().newTransformer();
+        transformer.transform(new DOMSource((Node) resourceNode), new StreamResult(writer));
+        return writer.toString();
+    }
+
+    /**
+     * Tests whether class with {@code *Param} annotated fields if used as a {@code BeanParam} annotated resource method parameter
+     * results in a correctly generated WADL.
+     *
+     * @throws Exception In case of any problem.
+     */
+    @Test
+    public void testBeanParamFullBean() throws Exception {
+        testBeanParamConstructorInitializedBean("wadlBeanParamTest");
+    }
+
+    /**
+     * Tests whether class with {@code *Param} annotated constructor parameters if used as a {@code BeanParam} annotated resource
+     * method parameter results in a correctly generated WADL.
+     *
+     * @throws Exception In case of any problem.
+     */
+    @Test
+    public void testBeanParamConstructorInitializedBean() throws Exception {
+        testBeanParamConstructorInitializedBean("wadlBeanParamConstructorInitializedTest");
+    }
+
+    /**
+     * Tests whether class with {@code *Param} annotated constructor parameters if used as a {@code BeanParam} annotated resource
+     * class field parameter results in a correctly generated WADL.
+     *
+     * @throws Exception In case of any problem.
+     */
+    @Test
+    public void testBeanParamFieldBean() throws Exception {
+        testBeanParamConstructorInitializedBean("wadlBeanParamFieldTest");
+    }
+
+    private void testBeanParamConstructorInitializedBean(String resource) throws Exception {
+        final Response response = target("/application.wadl").request().get();
+        final Document d = WadlResourceTest.extractWadlAsDocument(response);
+        final XPath xp = XPathFactory.newInstance().newXPath();
+        final SimpleNamespaceResolver nsContext = new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02");
+        xp.setNamespaceContext(nsContext);
+
+        final Diff diff = XMLUnit.compareXML(
+                nodeAsString(
+                        xp.evaluate("//wadl:resource[@path='wadlBeanParamReference']/wadl:resource", d,
+                                XPathConstants.NODE)),
+                nodeAsString(
+                        xp.evaluate("//wadl:resource[@path='" + resource + "']/wadl:resource", d,
+                                XPathConstants.NODE))
+        );
+        XMLUnit.setXpathNamespaceContext(
+                new SimpleNamespaceContext(ImmutableMap.of("wadl", "http://wadl.dev.java.net/2009/02")));
+        diff.overrideElementQualifier(elementQualifier);
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+
+    @Path("wadlBeanParamReference")
+    private static class ReferenceResourceBeanParam {
+
+        @QueryParam("classFieldQueryParam")
+        private String classFieldQueryParam;
+
+        //////////////////////////////////////////////////////////////////
+        // following fields make this class compatible with 'FullBean', //
+        // 'ConstructorInitializedBean' and 'SmallBean'                 //
+        @HeaderParam("header")
+        private String headerParam;
+
+        @MatrixParam("matrix")
+        private String matrixParam;
+
+        @Encoded
+        @QueryParam("query")
+        private String queryParam;
+
+        @CookieParam("cookie")
+        private String cookie;
+
+        @FormParam("form")
+        private String formParam;
+
+        @POST
+        @Path("singleBean/{path}")
+        public String postBeanParam(/* pathParam is also extracted from FullBean */
+                                    @PathParam("path") String pathParam,
+                                    @QueryParam("methodParam") int methodParam,
+                                    @HeaderParam("header") String duplicateHeaderParam) {
+            return "";
+        }
+
+    }
+
+    @Path("wadlBeanParamTest")
+    private static class TestResourceBeanParam {
+
+        @QueryParam("classFieldQueryParam")
+        private String classFieldQueryParam;
+
+        @POST
+        @Path("singleBean/{path}")
+        public String postBeanParam(@BeanParam WadlFullBean bean,
+                                    @BeanParam WadlSmallBean wadlSmallBean,
+                                    @QueryParam("methodParam") int methodParam) {
+            return "";
+        }
+
+    }
+
+    @Path("wadlBeanParamConstructorInitializedTest")
+    private static class TestResourceConstructorInitializedBeanParam {
+
+        @QueryParam("classFieldQueryParam")
+        private String classFieldQueryParam;
+
+        @POST
+        @Path("singleBean/{path}")
+        public String postBeanParam(@BeanParam WadlConstructorInitializedBean bean,
+                                    @BeanParam WadlSmallBean wadlSmallBean,
+                                    @QueryParam("methodParam") int methodParam) {
+            return "";
+        }
+
+    }
+
+    @Path("wadlBeanParamFieldTest")
+    private static class TestResourceFieldBeanParam {
+
+        @QueryParam("classFieldQueryParam")
+        private String classFieldQueryParam;
+
+        @BeanParam
+        private WadlConstructorInitializedBean bean;
+
+        @BeanParam
+        private WadlSmallBean wadlSmallBean;
+
+        @POST
+        @Path("singleBean/{path}")
+        public String postBeanParam(@QueryParam("methodParam") int methodParam) {
+            return "";
+        }
+
+    }
+
+    /**
+     * The purpose of this unknown annotation is to verify that a usage of an unknown annotation in a {@link BeanParam} annotated
+     * class does not cause a failure during WADL generation.
+     */
+    @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @Documented
+    @interface WadlUnknownAnnotation {
+
+        String value();
+    }
+
+    public static class WadlSmallBean {
+
+        @HeaderParam("header")
+        private String headerParam;
+
+        @PathParam("path")
+        private String pathParam;
+
+        public WadlSmallBean(WadlFullBean bean) {
+            headerParam = bean.getHeaderParam();
+            pathParam = bean.getPathParam();
+        }
+    }
+
+    public static class WadlEncodedBean {
+
+        @MatrixParam("matrix")
+        private String matrixParam;
+
+        @Encoded
+        @QueryParam("query")
+        private String queryParam;
+
+        public WadlEncodedBean(String matrixParam, String queryParam) {
+            this.matrixParam = matrixParam;
+            this.queryParam = queryParam;
+        }
+
+    }
+
+    public static class WadlFullBean {
+
+        @HeaderParam("header")
+        private String headerParam;
+
+        @PathParam("path")
+        private String pathParam;
+
+        @MatrixParam("matrix")
+        private String matrixParam;
+
+        @QueryParam("query")
+        private String queryParam;
+
+        @CookieParam("cookie")
+        private String cookie;
+
+        @FormParam("form")
+        private String formParam;
+
+        @WadlUnknownAnnotation("unknown")
+        private String unknownAnnotationParam;
+
+        @Context
+        private Request request;
+
+        private boolean overrideRequestNull;
+
+        public String getCookie() {
+            return cookie;
+        }
+
+        public void setCookie(String cookie) {
+            this.cookie = cookie;
+        }
+
+        public String getFormParam() {
+            return formParam;
+        }
+
+        public void setFormParam(String formParam) {
+            this.formParam = formParam;
+        }
+
+        public String getHeaderParam() {
+            return headerParam;
+        }
+
+        public void setHeaderParam(String headerParam) {
+            this.headerParam = headerParam;
+        }
+
+        public String getMatrixParam() {
+            return matrixParam;
+        }
+
+        public void setMatrixParam(String matrixParam) {
+            this.matrixParam = matrixParam;
+        }
+
+        public String getPathParam() {
+            return pathParam;
+        }
+
+        public void setPathParam(String pathParam) {
+            this.pathParam = pathParam;
+        }
+
+        public String getQueryParam() {
+            return queryParam;
+        }
+
+        public void setQueryParam(String queryParam) {
+            this.queryParam = queryParam;
+        }
+
+        public Request getRequest() {
+            return request;
+        }
+
+        public void setRequest(Request request) {
+            this.request = request;
+        }
+
+        public boolean isOverrideRequestNull() {
+            return overrideRequestNull;
+        }
+
+        public void setOverrideRequestNull(boolean overrideRequestNull) {
+            this.overrideRequestNull = overrideRequestNull;
+        }
+
+        public String getUnknownAnnotationParam() {
+            return unknownAnnotationParam;
+        }
+
+        public void setUnknownAnnotationParam(String unknownAnnotationParam) {
+            this.unknownAnnotationParam = unknownAnnotationParam;
+        }
+    }
+
+    public static class WadlConstructorInitializedBean {
+
+        private String headerParam;
+        private String pathParam;
+        private String matrixParam;
+        private String queryParam;
+        private String cookie;
+        private String formParam;
+        private String unknownAnnotationParam;
+        private Request request;
+
+        public WadlConstructorInitializedBean(@CookieParam("cookie") String cookie,
+                                              @FormParam("form") String formParam,
+                                              @HeaderParam("header") String headerParam,
+                                              @MatrixParam("matrix") String matrixParam,
+                                              @QueryParam("query") String queryParam,
+                                              @PathParam("path") String pathParam,
+                                              @WadlUnknownAnnotation("unknown") String unknownAnnotationParam,
+                                              @Context Request request) {
+            this.cookie = cookie;
+            this.formParam = formParam;
+            this.headerParam = headerParam;
+            this.matrixParam = matrixParam;
+            this.queryParam = queryParam;
+            this.pathParam = pathParam;
+            this.unknownAnnotationParam = unknownAnnotationParam;
+            this.request = request;
+        }
+
+        private boolean overrideRequestNull;
+
+        public String getCookie() {
+            return cookie;
+        }
+
+        public void setCookie(String cookie) {
+            this.cookie = cookie;
+        }
+
+        public String getFormParam() {
+            return formParam;
+        }
+
+        public void setFormParam(String formParam) {
+            this.formParam = formParam;
+        }
+
+        public String getHeaderParam() {
+            return headerParam;
+        }
+
+        public void setHeaderParam(String headerParam) {
+            this.headerParam = headerParam;
+        }
+
+        public String getMatrixParam() {
+            return matrixParam;
+        }
+
+        public void setMatrixParam(String matrixParam) {
+            this.matrixParam = matrixParam;
+        }
+
+        public String getPathParam() {
+            return pathParam;
+        }
+
+        public void setPathParam(String pathParam) {
+            this.pathParam = pathParam;
+        }
+
+        public String getQueryParam() {
+            return queryParam;
+        }
+
+        public void setQueryParam(String queryParam) {
+            this.queryParam = queryParam;
+        }
+
+        public Request getRequest() {
+            return request;
+        }
+
+        public void setRequest(Request request) {
+            this.request = request;
+        }
+
+        public boolean isOverrideRequestNull() {
+            return overrideRequestNull;
+        }
+
+        public void setOverrideRequestNull(boolean overrideRequestNull) {
+            this.overrideRequestNull = overrideRequestNull;
+        }
+
+        public String getUnknownAnnotationParam() {
+            return unknownAnnotationParam;
+        }
+
+        public void setUnknownAnnotationParam(String unknownAnnotationParam) {
+            this.unknownAnnotationParam = unknownAnnotationParam;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlEmptyMediaTypeTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlEmptyMediaTypeTest.java
new file mode 100644
index 0000000..085c20c
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlEmptyMediaTypeTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2018 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.wadl;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests, that Jersey returns wildcard mediaType in case no response representation was specified.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class WadlEmptyMediaTypeTest extends JerseyTest {
+
+    @Path("test")
+    public static class WadlEmptyMediaTypeTestResource {
+        @Path("getEmpty")
+        @GET
+        public String getEmpty() {
+            return "No @Produces annotation";
+        }
+
+        @Path("getText")
+        @Produces("text/plain")
+        @GET
+        public String getText() {
+            return "Produces text/plain";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(WadlEmptyMediaTypeTestResource.class);
+    }
+
+    @Test
+    public void testOverride() throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {
+        WebTarget target = target("/application.wadl");
+        String wadl = target.request().get(String.class);
+
+        InputSource is = new InputSource(new StringReader(wadl));
+        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
+
+        XPath xpath = XPathFactory.newInstance().newXPath();
+        String val = xpath.evaluate("//method[@id='getEmpty']/response/representation/@mediaType", document);
+        assertEquals("*/*", val);
+
+        val = xpath.evaluate("//method[@id='getText']/response/representation/@mediaType", document);
+        assertEquals("text/plain", val);
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
new file mode 100644
index 0000000..be2115c
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java
@@ -0,0 +1,1310 @@
+/*
+ * Copyright (c) 2010, 2018 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.wadl;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import javax.inject.Named;
+import javax.xml.XMLConstants;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.util.SaxHelper;
+import org.glassfish.jersey.internal.util.SimpleNamespaceResolver;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.wadl.WadlApplicationContext;
+import org.glassfish.jersey.server.wadl.WadlGenerator;
+import org.glassfish.jersey.server.wadl.config.WadlGeneratorConfig;
+import org.glassfish.jersey.server.wadl.config.WadlGeneratorDescription;
+import org.glassfish.jersey.server.wadl.internal.WadlGeneratorImpl;
+import org.glassfish.jersey.server.wadl.internal.WadlUtils;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.ElementQualifier;
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+
+import com.sun.research.ws.wadl.Method;
+import com.sun.research.ws.wadl.Param;
+import com.sun.research.ws.wadl.Request;
+import com.sun.research.ws.wadl.Resource;
+import com.sun.research.ws.wadl.Resources;
+
+/**
+ * WADL use case tests.
+ *
+ * @author Marc Hadley
+ * @author Miroslav Fuksa
+ * @author Michal Gajdos
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        WadlResourceTest.Wadl1Test.class,
+        WadlResourceTest.Wadl2Test.class,
+        WadlResourceTest.Wadl3Test.class,
+        WadlResourceTest.Wadl4Test.class,
+        WadlResourceTest.Wadl5Test.class,
+        WadlResourceTest.Wadl6Test.class,
+        WadlResourceTest.Wadl7Test.class,
+        WadlResourceTest.Wadl8Test.class,
+        WadlResourceTest.Wadl9Test.class,
+        WadlResourceTest.Wadl10Test.class,
+        WadlResourceTest.Wadl11Test.class,
+})
+public class WadlResourceTest {
+
+    /**
+     * Extracts WADL as {@link Document} from given {@link Response}.
+     *
+     * @param response The response to extract {@code Document} from.
+     * @return The extracted {@code Document}.
+     * @throws ParserConfigurationException In case of parser configuration issues.
+     * @throws SAXException                 In case of parsing issues.
+     * @throws IOException                  In case of IO error.
+     */
+    static Document extractWadlAsDocument(final Response response) throws ParserConfigurationException, SAXException,
+            IOException {
+        assertEquals(200, response.getStatus());
+        final File tmpFile = response.readEntity(File.class);
+        final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+        bf.setNamespaceAware(true);
+        bf.setValidating(false);
+        if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+            bf.setXIncludeAware(false);
+        }
+        final DocumentBuilder b = bf.newDocumentBuilder();
+        final Document d = b.parse(tmpFile);
+        Wadl5Test.printSource(new DOMSource(d));
+        return d;
+    }
+
+    private static String nodeAsString(final Object resourceNode) throws TransformerException {
+        StringWriter writer = new StringWriter();
+        Transformer transformer = TransformerFactory.newInstance().newTransformer();
+        transformer.transform(new DOMSource((Node) resourceNode), new StreamResult(writer));
+        return writer.toString();
+    }
+
+    public static class Wadl7Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(TestRootResource.class);
+        }
+
+        @Path("root")
+        public static class TestRootResource {
+
+            @Path("{template}")
+            @GET
+            public String getSub() {
+                return "get";
+            }
+
+            @GET
+            public String get() {
+                return "getroot";
+            }
+        }
+
+        @Test
+        public void testPathTemplateInSubResourceMethod() throws ParserConfigurationException, SAXException, IOException,
+                XPathExpressionException {
+            final Response response = target("root/foo").request(MediaTypes.WADL_TYPE).options();
+            assertEquals(200, response.getStatus());
+        }
+
+        @Test
+        public void testPathTemplateInSubResourceMethod2() throws ParserConfigurationException, SAXException, IOException,
+                XPathExpressionException {
+            final Response response = target("root").request(MediaTypes.WADL_TYPE).options();
+            assertEquals(200, response.getStatus());
+        }
+    }
+
+    public static class Wadl5Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(WidgetsResource.class, ExtraResource.class);
+        }
+
+        @Path("foo")
+        public static class ExtraResource {
+
+            @GET
+            @Produces("application/xml")
+            public String getRep() {
+                return null;
+            }
+        }
+
+        @Path("widgets")
+        public static class WidgetsResource {
+
+            @GET
+            @Produces({"application/xml", "application/json"})
+            public String getWidgets() {
+                return null;
+            }
+
+            @POST
+            @Consumes({"application/xml"})
+            @Produces({"application/xml", "application/json"})
+            public String createWidget(final String bar, @MatrixParam("test") final String test) {
+                return bar;
+            }
+
+            @PUT
+            @Path("{id}")
+            @Consumes("application/xml")
+            public void updateWidget(final String bar, @PathParam("id") final int id) {
+            }
+
+            @GET
+            @Path("{id}")
+            @Produces({"application/xml", "application/json"})
+            public String getWidget(@PathParam("id") final int id) {
+                return null;
+            }
+
+            @DELETE
+            @Path("{id}")
+            public void deleteWidget(@PathParam("id") final int id) {
+            }
+
+            @Path("{id}/verbose")
+            public ExtraResource getVerbose(@PathParam("id") final int id) {
+                return new ExtraResource();
+            }
+        }
+
+        @Test
+        public void testDisableWadl() throws ExecutionException, InterruptedException {
+            final ResourceConfig rc = new ResourceConfig(WidgetsResource.class, ExtraResource.class);
+            rc.property(ServerProperties.WADL_FEATURE_DISABLE, true);
+
+            final ApplicationHandler applicationHandler = new ApplicationHandler(rc);
+
+            final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest(
+                    URI.create("/"), URI.create("/application.wadl"),
+                    "GET", null, new MapPropertiesDelegate())).get();
+
+            assertEquals(404, containerResponse.getStatus());
+        }
+
+        @Test
+        public void testEnableWadl() throws ExecutionException, InterruptedException {
+            final ResourceConfig rc = new ResourceConfig(WidgetsResource.class, ExtraResource.class);
+            rc.property(ServerProperties.WADL_FEATURE_DISABLE, false);
+
+            final ApplicationHandler applicationHandler = new ApplicationHandler(rc);
+
+            final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest(
+                    URI.create("/"), URI.create("/application.wadl"),
+                    "GET", null, new MapPropertiesDelegate())).get();
+
+            assertEquals(200, containerResponse.getStatus());
+        }
+
+        public static void printSource(final Source source) {
+            try {
+                System.out.println("---------------------");
+                final Transformer trans = TransformerFactory.newInstance().newTransformer();
+                final Properties oprops = new Properties();
+                oprops.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
+                oprops.put(OutputKeys.INDENT, "yes");
+                oprops.put(OutputKeys.METHOD, "xml");
+                trans.setOutputProperties(oprops);
+                trans.transform(source, new StreamResult(System.out));
+            } catch (final Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public static String getXmlSchemaNamespacePrefix(final Node elementNode) {
+            final NamedNodeMap attributes = elementNode.getAttributes();
+
+            for (int i = 0; i < attributes.getLength(); i++) {
+                final Node item = attributes.item(i);
+                if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(item.getNodeValue())) {
+                    return item.getLocalName();
+                }
+            }
+
+            return "xs";
+        }
+
+        /**
+         * Test WADL generation.
+         *
+         * @throws Exception in case of unexpected test failure.
+         */
+        @Test
+        public void testGetWadl() throws Exception {
+            final File tmpFile = target("application.wadl").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                    .request().get(File.class);
+
+            final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+            bf.setNamespaceAware(true);
+            bf.setValidating(false);
+            if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+                bf.setXIncludeAware(false);
+            }
+            final DocumentBuilder b = bf.newDocumentBuilder();
+            final Document d = b.parse(tmpFile);
+            printSource(new DOMSource(d));
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+            // check base URI
+            String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+            // check total number of resources is 4
+            val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+            assertEquals("6", val);
+            // check only once resource with for {id}
+            val = (String) xp.evaluate("count(//wadl:resource[@path='{id}'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check only once resource with for {id}/verbose
+            val = (String) xp.evaluate("count(//wadl:resource[@path='{id}/verbose'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check only once resource with for widgets
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check 3 methods for {id}
+            val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("6", val);
+            // check 2 methods for widgets
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("5", val);
+            // check 1 matrix param on resource
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:param[@name='test'])", d,
+                    XPathConstants.STRING);
+            assertEquals("1", val);
+            // check type of {id} is int
+            String prefix = getXmlSchemaNamespacePrefix(
+                    (Node) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']", d, XPathConstants.NODE));
+            val = (String) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']/@type", d, XPathConstants.STRING);
+            assertEquals(val, (prefix == null ? "" : prefix + ":") + "int");
+            // check number of output representations is two
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='GET']/wadl:response/wadl"
+                    + ":representation)", d, XPathConstants.STRING);
+            assertEquals("2", val);
+            // check number of output representations is one
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='POST']/wadl:request/wadl"
+                    + ":representation)", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check type of {id}/verbose is int
+            prefix = getXmlSchemaNamespacePrefix(
+                    (Node) xp.evaluate("//wadl:resource[@path='{id}/verbose']/wadl:param[@name='id']", d, XPathConstants.NODE));
+            val = (String) xp.evaluate("//wadl:resource[@path='{id}/verbose']/wadl:param[@name='id']/@type", d,
+                    XPathConstants.STRING);
+            assertEquals(val, (prefix == null ? "" : prefix + ":") + "int");
+        }
+
+        @Test
+        public void testLastModifiedGET() {
+            final WebTarget target = target("/application.wadl");
+
+            final Response r = target.queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request().get(Response.class);
+            assertTrue(r.getHeaders().containsKey("Last-modified"));
+        }
+
+        @Test
+        public void testLastModifiedOPTIONS() {
+            final WebTarget target = target("/widgets/3/verbose");
+
+            final Response r = target.queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request(MediaTypes.WADL_TYPE)
+                    .options();
+            System.out.println(r.readEntity(String.class));
+            assertTrue(r.getHeaders().containsKey("Last-modified"));
+        }
+
+        @Test
+        public void testOptionsResourceWadl() throws ParserConfigurationException, XPathExpressionException, IOException,
+                SAXException {
+            // test WidgetsResource
+            Response response = target("/widgets").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                    .request(MediaTypes.WADL_TYPE).options();
+            assertEquals(200, response.getStatus());
+            File tmpFile = response.readEntity(File.class);
+            final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+            bf.setNamespaceAware(true);
+            bf.setValidating(false);
+            if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+                bf.setXIncludeAware(false);
+            }
+            DocumentBuilder b = bf.newDocumentBuilder();
+            Document d = b.parse(tmpFile);
+            printSource(new DOMSource(d));
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+            // check base URI
+            String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+            // check total number of resources is 3 (no ExtraResource details included)
+            val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+            assertEquals("3", val);
+            // check only once resource with for {id}
+            val = (String) xp.evaluate("count(//wadl:resource[@path='{id}'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check only once resource with for {id}/verbose
+            val = (String) xp.evaluate("count(//wadl:resource[@path='{id}/verbose'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check only once resource with for widgets
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check 3 methods for {id}
+            val = (String) xp.evaluate("count(//wadl:resource[@path='{id}']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("6", val);
+            // check 2 methods for widgets
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("5", val);
+            // check type of {id} is int
+            final String prefix = getXmlSchemaNamespacePrefix(
+                    (Node) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']", d, XPathConstants.NODE));
+            val = (String) xp.evaluate("//wadl:resource[@path='{id}']/wadl:param[@name='id']/@type", d, XPathConstants.STRING);
+            assertEquals(val, (prefix == null ? "" : prefix + ":") + "int");
+            // check number of output representations is two
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='GET']/wadl:response/wadl"
+                    + ":representation)", d, XPathConstants.STRING);
+            assertEquals("2", val);
+            // check number of output representations is one
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets']/wadl:method[@name='POST']/wadl:request/wadl"
+                    + ":representation)", d, XPathConstants.STRING);
+            assertEquals("1", val);
+
+            response = target("/foo").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                    .request(MediaTypes.WADL_TYPE).options();
+            assertEquals(200, response.getStatus());
+            tmpFile = response.readEntity(File.class);
+            b = bf.newDocumentBuilder();
+            d = b.parse(tmpFile);
+            printSource(new DOMSource(d));
+            // check base URI
+            val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+            // check total number of resources is 1 (no ExtraResource details included)
+            val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check only once resource with path foo
+            val = (String) xp.evaluate("count(//wadl:resource[@path='foo'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check 1 methods for foo
+            val = (String) xp.evaluate("count(//wadl:resource[@path='foo']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("4", val);
+        }
+
+        @Test
+        public void testOptionsLocatorWadl() throws ParserConfigurationException, SAXException, IOException,
+                XPathExpressionException {
+
+            // test WidgetsResource
+            final File tmpFile = target("/widgets/3/verbose").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                    .request(MediaTypes.WADL_TYPE).options(File.class);
+            final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+            bf.setNamespaceAware(true);
+            bf.setValidating(false);
+            if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+                bf.setXIncludeAware(false);
+            }
+            final DocumentBuilder b = bf.newDocumentBuilder();
+            final Document d = b.parse(tmpFile);
+            printSource(new DOMSource(d));
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+            // check base URI
+            String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+            // check total number of resources is 1 (no ExtraResource details included)
+            val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+            assertEquals(val, "1");
+            // check only once resource with path
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3/verbose'])", d, XPathConstants.STRING);
+            assertEquals(val, "1");
+            // check 1 methods
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3/verbose']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("4", val);
+        }
+
+        @Test
+        public void testOptionsSubResourceWadl() throws ParserConfigurationException, SAXException, IOException,
+                XPathExpressionException {
+            // test WidgetsResource
+            final File tmpFile = target("/widgets/3").queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true")
+                    .request(MediaTypes.WADL_TYPE).options(File.class);
+            final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+            bf.setNamespaceAware(true);
+            bf.setValidating(false);
+            if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+                bf.setXIncludeAware(false);
+            }
+            final DocumentBuilder b = bf.newDocumentBuilder();
+            final Document d = b.parse(tmpFile);
+            printSource(new DOMSource(d));
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+            String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+            // check total number of resources is 1
+            val = (String) xp.evaluate("count(//wadl:resource)", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check only one resource with for {id}
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3'])", d, XPathConstants.STRING);
+            assertEquals("1", val);
+            // check 3 methods
+            val = (String) xp.evaluate("count(//wadl:resource[@path='widgets/3']/wadl:method)", d, XPathConstants.STRING);
+            assertEquals("6", val);
+        }
+
+        public static class MyWadlGeneratorConfig extends WadlGeneratorConfig {
+
+            @Override
+            public List<WadlGeneratorDescription> configure() {
+                return generator(MyWadlGenerator.class).descriptions();
+            }
+
+            public static class MyWadlGenerator extends WadlGeneratorImpl {
+
+                public static final String CUSTOM_RESOURCES_BASE_URI = "http://myBaseUri";
+
+                @Override
+                public Resources createResources() {
+                    final Resources resources = super.createResources();
+                    resources.setBase(CUSTOM_RESOURCES_BASE_URI);
+
+                    return resources;
+                }
+
+                @Override
+                public void setWadlGeneratorDelegate(final WadlGenerator delegate) {
+                    // nothing
+                }
+            }
+        }
+
+        /**
+         * Test overriding WADL's /application/resources/@base attribute.
+         *
+         * @throws Exception in case of unexpected test failure.
+         */
+        @Test
+        public void testCustomWadlResourcesBaseUri() throws Exception {
+            final ResourceConfig rc = new ResourceConfig(WidgetsResource.class, ExtraResource.class);
+            rc.property(ServerProperties.WADL_GENERATOR_CONFIG, MyWadlGeneratorConfig.class.getName());
+
+            final ApplicationHandler applicationHandler = new ApplicationHandler(rc);
+
+            final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest(
+                    URI.create("/"), URI.create("/application.wadl"),
+                    "GET", null, new MapPropertiesDelegate())).get();
+
+            final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+            bf.setNamespaceAware(true);
+            bf.setValidating(false);
+            if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+                bf.setXIncludeAware(false);
+            }
+            final DocumentBuilder b = bf.newDocumentBuilder();
+
+            ((ByteArrayInputStream) containerResponse.getEntity()).reset();
+
+            final Document d = b.parse((InputStream) containerResponse.getEntity());
+            printSource(new DOMSource(d));
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+            // check base URI
+            final String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, MyWadlGeneratorConfig.MyWadlGenerator.CUSTOM_RESOURCES_BASE_URI);
+        }
+
+        @Path("emptyproduces")
+        public static class EmptyProducesTestResource {
+
+            @PUT
+            @Produces({})
+            public Response put(final String str) {
+                return Response.ok().build();
+            }
+
+            @POST
+            @Path("/sub")
+            @Produces({})
+            public Response post(final String str) {
+                return Response.ok().build();
+            }
+        }
+
+        @Test
+        public void testEmptyProduces() throws Exception {
+            final ResourceConfig rc = new ResourceConfig(EmptyProducesTestResource.class);
+            rc.property(ServerProperties.WADL_FEATURE_DISABLE, false);
+
+            final ApplicationHandler applicationHandler = new ApplicationHandler(rc);
+
+            final ContainerResponse containerResponse = applicationHandler.apply(new ContainerRequest(
+                    URI.create("/"), URI.create("/application.wadl"),
+                    "GET", null, new MapPropertiesDelegate())).get();
+
+            assertEquals(200, containerResponse.getStatus());
+
+            ((ByteArrayInputStream) containerResponse.getEntity()).reset();
+
+            final DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+            bf.setNamespaceAware(true);
+            bf.setValidating(false);
+            if (!SaxHelper.isXdkDocumentBuilderFactory(bf)) {
+                bf.setXIncludeAware(false);
+            }
+            final DocumentBuilder b = bf.newDocumentBuilder();
+            final Document d = b.parse((InputStream) containerResponse.getEntity());
+            printSource(new DOMSource(d));
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+            final NodeList responseElements = (NodeList) xp.evaluate(
+                    "/wadl:application/wadl:resources[@path!='application.wadl']//wadl:method/wadl:response", d,
+                    XPathConstants.NODESET);
+
+            for (int i = 0; i < responseElements.getLength(); i++) {
+                final Node item = responseElements.item(i);
+
+                assertEquals(0, item.getChildNodes().getLength());
+                assertEquals(0, item.getAttributes().getLength());
+            }
+        }
+    }
+
+    public static class Wadl1Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(RootResource.class);
+        }
+
+        @Path("root")
+        public static class RootResource {
+
+            @Path("loc")
+            public Object getSub() {
+                return new SubResource();
+            }
+
+            @Path("switch")
+            @POST
+            public void switchMethod(@Context final WadlApplicationContext wadlApplicationContext) {
+                wadlApplicationContext.setWadlGenerationEnabled(!wadlApplicationContext.isWadlGenerationEnabled());
+
+            }
+        }
+
+        public static class SubResource {
+
+            @Path("loc")
+            public Object getSub() {
+                return new SubResource();
+            }
+
+            @GET
+            @Produces("text/plain")
+            public String hello() {
+                return "Hello World !";
+            }
+
+            @GET
+            @Path("sub")
+            @Produces("text/plain")
+            public String helloSub() {
+                return "Hello World !";
+            }
+        }
+
+        @Test
+        public void testRecursive() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+            _testRecursiveWadl("root/loc");
+        }
+
+        @Test
+        public void mytest() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+            final Response response = target().path("root/test").request().get();
+            System.out.println(response.readEntity(String.class));
+        }
+
+        @Test
+        public void testRecursive2() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+            _testRecursiveWadl("root/loc/loc");
+        }
+
+        private void _testRecursiveWadl(final String path) throws ParserConfigurationException, SAXException, IOException,
+                XPathExpressionException {
+            final Document d = extractWadlAsDocument(target(path).request(MediaTypes.WADL_TYPE).options());
+
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+            String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+
+            // check only one resource with for 'root/loc'
+            val = (String) xp.evaluate("count(//wadl:resource[@path='" + path + "'])", d, XPathConstants.STRING);
+            assertEquals(val, "1");
+        }
+
+    }
+
+    public static class Wadl2Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(RootResource1.class, RootResource2.class);
+        }
+
+        @Path("root1")
+        public static class RootResource1 {
+
+            @Path("loc")
+            public Wadl1Test.SubResource getSub() {
+                return new Wadl1Test.SubResource();
+            }
+        }
+
+        @Path("root2")
+        public static class RootResource2 {
+
+            @Path("loc")
+            public Wadl1Test.SubResource getSub() {
+                return new Wadl1Test.SubResource();
+            }
+        }
+
+        @Test
+        public void testRecursive2() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+            final Document d = extractWadlAsDocument(target("/application.wadl")
+                    .queryParam(WadlUtils.DETAILED_WADL_QUERY_PARAM, "true").request().get());
+
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+            String val = (String) xp.evaluate("/wadl:application/wadl:resources/@base", d, XPathConstants.STRING);
+            assertEquals(val, getBaseUri().toString());
+            // check only one resource with for 'root/loc'
+            val = (String) xp.evaluate("count(//wadl:resource[@path='loc'])", d, XPathConstants.STRING);
+            assertEquals("4", val);
+            // check for method with id of hello
+            val = (String) xp.evaluate("count(//wadl:resource[@path='loc']/wadl:method[@id='hello'])", d, XPathConstants.STRING);
+            assertEquals("2", val);
+        }
+    }
+
+    public static class Wadl3Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(FormResource.class);
+        }
+
+        @Path("form")
+        public static class FormResource {
+
+            @POST
+            @Consumes("application/x-www-form-urlencoded")
+            public void post(
+                    @FormParam("a") final String a,
+                    @FormParam("b") final String b,
+                    @FormParam("c") final JaxbBean c,
+                    @FormParam("c") final FormDataContentDisposition cdc,
+                    final Form form) {
+            }
+
+        }
+
+        @Test
+        public void testFormParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+            final Document d = extractWadlAsDocument(target("/application.wadl").request().get());
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+            final String requestPath = "//wadl:resource[@path='form']/wadl:method[@name='POST']/wadl:request";
+            final String representationPath = requestPath + "/wadl:representation";
+
+            // check number of request params is zero
+            int count = ((Double) xp.evaluate("count(" + requestPath + "/wadl:param)", d, XPathConstants.NUMBER)).intValue();
+            assertEquals(0, count);
+
+            // check number of request representations is one
+            count = ((Double) xp.evaluate("count(" + representationPath + ")", d, XPathConstants.NUMBER)).intValue();
+            assertEquals(1, count);
+
+            // check number of request representation params is three
+            count = ((Double) xp.evaluate("count(" + representationPath + "/wadl:param)", d, XPathConstants.NUMBER)).intValue();
+            assertEquals(3, count);
+
+            // check the style of the request representation param is 'query'
+            String val = (String) xp.evaluate(representationPath + "/wadl:param[@name='a']/@style", d, XPathConstants.STRING);
+            assertEquals("query", val);
+            val = (String) xp.evaluate(representationPath + "/wadl:param[@name='b']/@style", d, XPathConstants.STRING);
+            assertEquals("query", val);
+
+        }
+    }
+
+    public static class Wadl4Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(FieldParamResource.class);
+        }
+
+        @Path("fieldParam/{pp}")
+        public static class FieldParamResource {
+
+            @HeaderParam("hp")
+            String hp;
+            @MatrixParam("mp")
+            String mp;
+            @PathParam("pp")
+            String pp;
+
+            private String q;
+
+            @QueryParam("q")
+            public void setQ(final String q) {
+                this.q = q;
+            }
+
+            // these should not be included in WADL
+            @Context
+            UriInfo uriInfo;
+            @Named("fakeParam")
+            String fakeParam;
+
+            @GET
+            @Produces("text/plain")
+            public String get() {
+                return pp;
+            }
+
+        }
+
+        @Test
+        public void testFieldParam() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+            final String path = "fieldParam";
+            _testFieldAndSetterParam(target("/application.wadl").request().get(), path);
+        }
+
+        private static void _testFieldAndSetterParam(final Response response, final String path)
+                throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
+
+            final Document d = extractWadlAsDocument(response);
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+            final String resourcePath = String.format("//wadl:resource[@path='%s/{pp}']", path);
+            final String methodPath = resourcePath + "/wadl:method[@name='GET']";
+
+            // check number of resource methods is one
+            final int methodCount = ((Double) xp.evaluate("count(" + methodPath + ")", d, XPathConstants.NUMBER)).intValue();
+            assertThat("Unexpected number of methods on path '" + methodPath + "'", methodCount, equalTo(1));
+
+            final Map<String, String> paramStyles = new HashMap<>();
+            paramStyles.put("mp", "matrix");
+            paramStyles.put("pp", "template");
+
+            // check the number of resource params
+            final String resourceParamsCountXPath = String.format("count(%s/wadl:param)", resourcePath);
+            final int resourceParamsCount = ((Double) xp.evaluate(resourceParamsCountXPath, d, XPathConstants.NUMBER)).intValue();
+            assertThat("Number of resource parameters does not match.", resourceParamsCount, equalTo(2));
+
+            for (final Map.Entry<String, String> param : paramStyles.entrySet()) {
+
+                final String pName = param.getKey();
+                final String pStyle = param.getValue();
+
+                final String paramXPath = String.format("%s/wadl:param[@name='%s']", resourcePath, pName);
+
+                // check number of params is one
+                final int pc = ((Double) xp.evaluate("count(" + paramXPath + ")", d, XPathConstants.NUMBER)).intValue();
+                assertThat("Number of " + pStyle + " parameters '" + pName + "' does not match.", pc, equalTo(1));
+
+                // check the style of the param
+                final String style = (String) xp.evaluate(paramXPath + "/@style", d, XPathConstants.STRING);
+                assertThat("Parameter '" + pName + "' style does not match.", pStyle, equalTo(style));
+            }
+
+            paramStyles.clear();
+            paramStyles.put("hp", "header");
+            paramStyles.put("q", "query");
+
+            // check the number of request params
+            final String requestParamsCountXPath = String.format("count(%s/wadl:request/wadl:param)", methodPath);
+            final int requestParamsCount = ((Double) xp.evaluate(requestParamsCountXPath, d, XPathConstants.NUMBER)).intValue();
+            assertThat("Number of request parameters does not match.", requestParamsCount, equalTo(2));
+
+            for (final Map.Entry<String, String> param : paramStyles.entrySet()) {
+
+                final String pName = param.getKey();
+                final String pStyle = param.getValue();
+
+                final String paramXPath = String.format("%s/wadl:request/wadl:param[@name='%s']", methodPath, pName);
+
+                // check that the number of params is one
+                final int pc = ((Double) xp.evaluate("count(" + paramXPath + ")", d, XPathConstants.NUMBER)).intValue();
+                assertThat("Number of " + pStyle + " parameters '" + pName + "' does not match.", pc, equalTo(1));
+
+                // check the style of the param
+                final String style = (String) xp.evaluate(paramXPath + "/@style", d, XPathConstants.STRING);
+                assertThat("Parameter '" + pName + "' style does not match.", pStyle, equalTo(style));
+            }
+        }
+    }
+
+    /**
+     * Tests OPTIONS method on a resource method annotated with @Path and containing a leading '/'.
+     */
+    public static class Wadl6Test extends JerseyTest {
+
+        @Path("wadl6test")
+        public static class Resource {
+
+            @GET
+            @Path("foo1")
+            public String foo1() {
+                return "foo1";
+            }
+
+            @GET
+            @Path("/foo2")
+            public String foo2() {
+                return "foo2";
+            }
+
+            @GET
+            @Path("foo3/")
+            public String foo3() {
+                return "foo3";
+            }
+
+            @GET
+            @Path("/foo4/")
+            public String foo4() {
+                return "foo4";
+            }
+        }
+
+        @Override
+        protected Application configure() {
+            enable(TestProperties.LOG_TRAFFIC);
+            enable(TestProperties.DUMP_ENTITY);
+
+            return new ResourceConfig(Resource.class);
+        }
+
+        @Test
+        public void testGetWithPathAndLeadingSlash() throws Exception {
+            for (int i = 1; i < 5; i++) {
+                final String[] paths = {
+                        "foo" + i,
+                        "/foo" + i,
+                        "foo" + i + '/',
+                        "/foo" + i + '/'
+                };
+
+                for (final String path : paths) {
+                    final Response response = target("wadl6test")
+                            .path(path)
+                            .request("application/vnd.sun.wadl+xml")
+                            .options();
+                    assertEquals(200, response.getStatus());
+                    final String document = response.readEntity(String.class);
+
+                    // check that the resulting document contains a method element with id="fooX"
+                    assertTrue(document.replaceAll("\n", " ").matches(".*<method[^>]+id=\"foo" + i + "\"[^>]*>.*"));
+                }
+            }
+        }
+    }
+
+    public static class Wadl8Test extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(ResourceA.class, ResourceB.class, ResourceSpecific.class);
+        }
+
+        @Path("{a}")
+        public static class ResourceA {
+
+            @GET
+            public String getA() {
+                return "a";
+            }
+        }
+
+        @Path("{b}")
+        public static class ResourceB {
+
+            @POST
+            public String postB(final String str) {
+                return "b";
+            }
+        }
+
+        @Path("resource")
+        public static class ResourceSpecific {
+
+            @GET
+            @Path("{templateA}")
+            public String getTemplateA() {
+                return "template-a";
+            }
+
+            @POST
+            @Path("{templateB}")
+            public String postTemplateB(final String str) {
+                return "template-b";
+            }
+        }
+
+        @Test
+        @Ignore("JERSEY-1670: WADL Options invoked on resources with same template returns only methods from one of them.")
+        // TODO: fix
+        public void testWadlForAmbiguousResourceTemplates()
+                throws IOException, SAXException, ParserConfigurationException, XPathExpressionException {
+            final Response response = target().path("foo").request(MediaTypes.WADL_TYPE).options();
+            final Document d = extractWadlAsDocument(response);
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+            String result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='GET']/@id", d, XPathConstants.STRING);
+            assertEquals("getA", result);
+
+            result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='POST']/@id", d, XPathConstants.STRING);
+            assertEquals("postB", result);
+        }
+
+        @Test
+        @Ignore("JERSEY-1670: WADL Options invoked on resources with same template returns only methods from one of them.")
+        // TODO: fix
+        public void testWadlForAmbiguousChildResourceTemplates()
+                throws IOException, SAXException, ParserConfigurationException, XPathExpressionException {
+            final Response response = target().path("resource/bar").request(MediaTypes.WADL_TYPE).options();
+
+            final Document d = extractWadlAsDocument(response);
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02"));
+
+            String result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='GET']/@id", d, XPathConstants.STRING);
+            assertEquals("getTemplateA", result);
+
+            result = (String) xp.evaluate("//wadl:resource/wadl:method[@name='POST']/@id", d, XPathConstants.STRING);
+            assertEquals("postTemplateB", result);
+        }
+    }
+
+    /**
+     * Tests usage of property {@link ServerProperties#METAINF_SERVICES_LOOKUP_DISABLE}. Wadl is registered automatically every
+     * time and can be turned off only via {@link ServerProperties#WADL_FEATURE_DISABLE}.
+     */
+    public static class Wadl9Test extends JerseyTest {
+
+        @Path("wadl9test")
+        public static class Resource {
+
+            @GET
+            public String foo() {
+                return "foo";
+            }
+
+        } // class Resource
+
+        @Override
+        protected Application configure() {
+            final ResourceConfig resourceConfig = new ResourceConfig(Resource.class);
+            resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
+
+            return resourceConfig;
+        }
+
+        @Test
+        public void testWadl() throws Exception {
+            final Response response = target("/application.wadl").request().get();
+
+            assertThat(response.getStatus(), is(200));
+            assertThat(response.hasEntity(), is(true));
+        }
+
+    } // class Wadl9Test
+
+    /**
+     * Tests whether boolean getters have been generated with "is" prefix.
+     */
+    public static class Wadl10Test extends JerseyTest {
+
+        @Path("wadl10test")
+        public static class Resource {
+
+            @GET
+            public Boolean foo(@QueryParam("q") final Boolean q) {
+                return q;
+            }
+
+        } // class Resource
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(Resource.class);
+        }
+
+        @Test
+        public void testWadl() throws Exception {
+            final Response response = target("/application.wadl").request().get();
+
+            assertThat(response.getStatus(), is(200));
+            assertThat(response.hasEntity(), is(true));
+
+            final Method method = (Method) response.readEntity(com.sun.research.ws.wadl.Application.class) // wadl
+                    .getResources().get(0).getResource().get(0) // resource
+                    .getMethodOrResource().get(0); // method
+            final Param param = method.getRequest().getParam().get(0); // param
+
+            // not interested in returned value, only whether we can compile.
+            assertThat(param.isRequired(), notNullValue());
+            assertThat(param.isRepeating(), notNullValue());
+        }
+
+    } // class Wadl10Test
+
+    /**
+     * Tests whether unknown annotation affects a WADL correctness.
+     */
+    public static class Wadl11Test extends JerseyTest {
+
+        @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE})
+        @Retention(RetentionPolicy.RUNTIME)
+        @Inherited
+        public @interface UnknownAnnotation {
+        }
+
+        @Path("annotated")
+        @Consumes("application/json")
+        @UnknownAnnotation
+        public static class Annotated {
+
+            @Path("subresource")
+            @POST
+            @Produces("application/json")
+            @UnknownAnnotation
+            public Entity myMethod1(@UnknownAnnotation final Entity entity) {
+                return entity;
+            }
+
+            @Path("subresource")
+            @POST
+            @Produces("application/xml")
+            public Entity myMethod2(@UnknownAnnotation final Entity entity) {
+                return entity;
+            }
+
+        }
+
+        @Path("not-annotated")
+        @Consumes("application/json")
+        public static class Plain {
+
+            @Path("subresource")
+            @POST
+            @Produces("application/json")
+            public Entity myMethod1(final Entity entity) {
+                return entity;
+            }
+
+            @Path("subresource")
+            @POST
+            @Produces("application/xml")
+            public Entity myMethod2(final Entity entity) {
+                return entity;
+            }
+
+        }
+
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(Annotated.class, Plain.class);
+        }
+
+        @Test
+        public void testWadlIsComplete() throws Exception {
+            final Response response = target("/application.wadl").request().get();
+
+            assertThat(response.getStatus(), is(200));
+            assertThat(response.hasEntity(), is(true));
+
+            final com.sun.research.ws.wadl.Application application =
+                    response.readEntity(com.sun.research.ws.wadl.Application.class);
+
+            // "annotated/subresource"
+            final Resource resource =
+                    (Resource) application.getResources().get(0).getResource().get(0).getMethodOrResource().get(0);
+
+            assertThatMethodContainsRR(resource, "myMethod1", 0);
+            assertThatMethodContainsRR(resource, "myMethod2", 1);
+
+        }
+
+        private static void assertThatMethodContainsRR(final Resource resource, final String methodName, final int methodIndex) {
+            final Method method = (Method) resource.getMethodOrResource().get(methodIndex);
+
+            assertThat(method.getId(), equalTo(methodName));
+
+            final Request request = method.getRequest();
+            final List<com.sun.research.ws.wadl.Response> response = method.getResponse();
+
+            assertThat(request, notNullValue());
+            assertThat(response.isEmpty(), is(false));
+        }
+
+        @Test
+        public void testWadlIsSameForAnnotatedAndNot() throws Exception {
+
+            final Response response = target("/application.wadl").request().get();
+
+            final Document document = extractWadlAsDocument(response);
+
+            final XPath xp = XPathFactory.newInstance().newXPath();
+            final SimpleNamespaceResolver nsContext = new SimpleNamespaceResolver("wadl", "http://wadl.dev.java.net/2009/02");
+            xp.setNamespaceContext(nsContext);
+
+            final Diff diff = XMLUnit.compareXML(
+                    nodeAsString(
+                            xp.evaluate("//wadl:resource[@path='annotated']/wadl:resource", document,
+                                    XPathConstants.NODE)),
+                    nodeAsString(
+                            xp.evaluate("//wadl:resource[@path='not-annotated']/wadl:resource", document,
+                                    XPathConstants.NODE))
+            );
+            XMLUnit.setXpathNamespaceContext(
+                    new SimpleNamespaceContext(ImmutableMap.of("wadl", "http://wadl.dev.java.net/2009/02")));
+            final ElementQualifier elementQualifier = new RecursiveElementNameAndTextQualifier();
+            diff.overrideElementQualifier(elementQualifier);
+            XMLAssert.assertXMLEqual(diff, true);
+
+        }
+
+    } // class Wadl11Test
+
+
+    @XmlRootElement(name = "jaxbBean")
+    public class JaxbBean {
+
+        public String value;
+
+        public JaxbBean() {
+        }
+
+        public JaxbBean(String str) {
+            value = str;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof JaxbBean)) {
+                return false;
+            }
+            return ((JaxbBean) o).value.equals(value);
+        }
+
+        public String toString() {
+            return "JAXBClass: " + value;
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/resources/CustomViewableContext/index.testp b/tests/e2e-server/src/test/resources/CustomViewableContext/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/CustomViewableContext/index.testp
diff --git a/tests/e2e-server/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyReader b/tests/e2e-server/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyReader
new file mode 100644
index 0000000..681227c
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyReader
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.e2e.server.AbstractDisableMetainfServicesLookupTest$UselessMessageProvider
\ No newline at end of file
diff --git a/tests/e2e-server/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter b/tests/e2e-server/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter
new file mode 100644
index 0000000..681227c
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.e2e.server.AbstractDisableMetainfServicesLookupTest$UselessMessageProvider
\ No newline at end of file
diff --git a/tests/e2e-server/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable b/tests/e2e-server/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable
new file mode 100644
index 0000000..dcdd754
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.e2e.server.monitoring.ApplicationInfoTest$ForcedAutoDiscoverableImpl
diff --git a/tests/e2e-server/src/test/resources/ValidationMessages.properties b/tests/e2e-server/src/test/resources/ValidationMessages.properties
new file mode 100644
index 0000000..1d440e9
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/ValidationMessages.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2012, 2018 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
+#
+
+org.glassfish.jersey.tests.e2e.server.validation.NonEmptyNames.message=at least one of the names is empty
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest/ErrorTemplateResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest/ErrorTemplateResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest/ErrorTemplateResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/relative.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/relative.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/relative.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/absolute.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/absolute.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/absolute.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/relative.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/relative.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/relative.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/404.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/404.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/404.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/WebAppExceptionMapper/406.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/WebAppExceptionMapper/406.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/WebAppExceptionMapper/406.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.def b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.def
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.def
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/absolute.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/absolute.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/absolute.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/index.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/index.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/index.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/relative.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/relative.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/relative.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.def b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.def
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.def
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.def b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.def
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.def
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.abc b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.abc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.abc
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/relative.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/relative.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/relative.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/absolute.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/absolute.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/absolute.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/relative.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/relative.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/relative.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplate.override.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplate.override.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplate.override.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.inherit.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.inherit.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.inherit.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplate.override.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplate.override.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplate.override.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.inherit.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.inherit.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.inherit.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ExplicitTemplate.show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ExplicitTemplate.show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ExplicitTemplate.show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitTemplate.index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitTemplate.index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitTemplate.index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithGetTemplate.index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithGetTemplate.index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithGetTemplate.index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithSubResourceGetTemplate.sub.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithSubResourceGetTemplate.sub.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithSubResourceGetTemplate.sub.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ExplicitTemplate/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ExplicitTemplate/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ExplicitTemplate/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest/Handler/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest/Handler/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest/Handler/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/get.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/get.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/get.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/get.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/get.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/get.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSingletonResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSingletonResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSingletonResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSubSubResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSubSubResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSubSubResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest/ImplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest/ImplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest/ImplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplate/override.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplate/override.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplate/override.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/inherit.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/inherit.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/inherit.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/override.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/override.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/override.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplate/override.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplate/override.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplate/override.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/inherit.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/inherit.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/inherit.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/override.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/override.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/override.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/FreemarkerResource.ftl b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/FreemarkerResource.ftl
new file mode 100644
index 0000000..c38689f
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/FreemarkerResource.ftl
@@ -0,0 +1 @@
+Model:${user}
\ No newline at end of file
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/MustacheResource.mustache b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/MustacheResource.mustache
new file mode 100644
index 0000000..e0d8fbf
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/MustacheResource.mustache
@@ -0,0 +1 @@
+Model:{{user}}
\ No newline at end of file
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ResolvingClassViewProcessorTest/ResolvingClass/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ResolvingClassViewProcessorTest/ResolvingClass/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ResolvingClassViewProcessorTest/ResolvingClass/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/getAsHTML b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/getAsHTML
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/getAsHTML
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/getAsHTML b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/getAsHTML
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/getAsHTML
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/getAsHTML b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/getAsHTML
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/getAsHTML
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/absolute/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/absolute/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/absolute/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithGetTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithGetTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithGetTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/show.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/show.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/show.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithGetTemplate/index.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithGetTemplate/index.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithGetTemplate/index.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp
diff --git a/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest/Resource/index.fct b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest/Resource/index.fct
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/e2e-server/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest/Resource/index.fct
diff --git a/tests/e2e-testng/pom.xml b/tests/e2e-testng/pom.xml
new file mode 100644
index 0000000..4aa82b5
--- /dev/null
+++ b/tests/e2e-testng/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e-testng</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e-testng</name>
+
+    <description>Jersey E2E (TestNG) tests</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-testng</artifactId>
+                        <version>2.17</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/AbstractParallelTest.java b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/AbstractParallelTest.java
new file mode 100644
index 0000000..64cd925
--- /dev/null
+++ b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/AbstractParallelTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2018 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;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTestNg;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Base for TestNG parallel tests. Contains one singleton resource with GET method returning incremental sequence of integers.
+ * Extensions has to implement {@link #testValue(Integer)}.
+ *
+ * @author Michal Gajdos
+ */
+@Test(threadPoolSize = 5, invocationCount = 13)
+public abstract class AbstractParallelTest extends JerseyTestNg {
+
+    @Path("/")
+    @Singleton
+    @Produces("text/plain")
+    public static class Resource {
+
+        private AtomicInteger ai = new AtomicInteger(0);
+
+        @GET
+        public int get() {
+            return ai.getAndIncrement();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        // Find first available port.
+        forceSet(TestProperties.CONTAINER_PORT, "0");
+
+        return new ResourceConfig(Resource.class);
+    }
+
+    public void test1() throws Exception {
+        test();
+    }
+
+    public void test2() throws Exception {
+        test();
+    }
+
+    public void test3() throws Exception {
+        test();
+    }
+
+    private void test() {
+        final Response response = target().request().get();
+
+        assertEquals(response.getStatus(), 200);
+        testValue(response.readEntity(Integer.class));
+    }
+
+    protected abstract void testValue(final Integer actual);
+}
diff --git a/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/BeforeClassParallelTest.java b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/BeforeClassParallelTest.java
new file mode 100644
index 0000000..8ba02b6
--- /dev/null
+++ b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/BeforeClassParallelTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2018 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;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.glassfish.jersey.test.ContainerPerClassTestNgStrategy;
+import org.glassfish.jersey.test.spi.TestNgStrategy;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Runs a set of tests (in parallel) against single test container instance.
+ *
+ * @author Michal Gajdos
+ */
+public class BeforeClassParallelTest extends AbstractParallelTest {
+
+    private ConcurrentMap<Integer, String> values = new ConcurrentHashMap<>();
+
+    @BeforeClass
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @AfterClass
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Override
+    protected TestNgStrategy configureStrategy() {
+        return new ContainerPerClassTestNgStrategy();
+    }
+
+    @Override
+    protected void testValue(final Integer actual) {
+        final String name = values.putIfAbsent(actual, Thread.currentThread().getName());
+
+        assertThat(String.format("Value %d has already been returned by client in thread %s.", actual, name),
+                name, nullValue());
+    }
+}
diff --git a/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/BeforeMethodParallelTest.java b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/BeforeMethodParallelTest.java
new file mode 100644
index 0000000..650d2d4
--- /dev/null
+++ b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/BeforeMethodParallelTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014, 2018 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;
+
+import org.glassfish.jersey.test.ContainerPerMethodTestNgStrategy;
+import org.glassfish.jersey.test.spi.TestNgStrategy;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Runs a set of tests (in parallel) each against separate test container instance.
+ *
+ * @author Michal Gajdos
+ */
+public class BeforeMethodParallelTest extends AbstractParallelTest {
+
+    @BeforeMethod
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @AfterMethod
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Override
+    protected TestNgStrategy configureStrategy() {
+        return new ContainerPerMethodTestNgStrategy();
+    }
+
+    @Override
+    protected void testValue(final Integer actual) {
+        assertThat(actual, is(0));
+    }
+}
diff --git a/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/ContainerPerClassTest.java b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/ContainerPerClassTest.java
new file mode 100644
index 0000000..7cf2eb7
--- /dev/null
+++ b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/ContainerPerClassTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014, 2018 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;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTestNg;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Tests that container is created only once per class and each test method sends request to the same container.
+ *
+ * @author Michal Gajdos
+ */
+public class ContainerPerClassTest extends JerseyTestNg.ContainerPerClassTest {
+
+    @Path("/")
+    @Singleton
+    @Produces("text/plain")
+    public static class Resource {
+
+        private int i = 1;
+
+        @GET
+        public int get() {
+            return i++;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test(priority = 1)
+    public void first() throws Exception {
+        test(1);
+    }
+
+    @Test(priority = 2)
+    public void second() throws Exception {
+        test(2);
+    }
+
+    @Test(priority = 3)
+    public void third() throws Exception {
+        test(3);
+    }
+
+    private void test(final Integer expected) {
+        final Response response = target().request().get();
+
+        assertEquals(response.getStatus(), 200);
+        assertEquals(response.readEntity(Integer.class), expected);
+    }
+}
diff --git a/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/ContainerPerMethodTest.java b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/ContainerPerMethodTest.java
new file mode 100644
index 0000000..9b2948e
--- /dev/null
+++ b/tests/e2e-testng/src/test/java/org/glassfish/jersey/tests/e2e/ContainerPerMethodTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014, 2018 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;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTestNg;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Tests that new container is created for each test method.
+ *
+ * @author Michal Gajdos
+ */
+public class ContainerPerMethodTest extends JerseyTestNg.ContainerPerMethodTest {
+
+    @Path("/")
+    @Singleton
+    @Produces("text/plain")
+    public static class Resource {
+
+        private int i = 1;
+
+        @GET
+        public int get() {
+            return i++;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test(priority = 1)
+    public void first() throws Exception {
+        test(1);
+    }
+
+    @Test(priority = 2)
+    public void second() throws Exception {
+        test(1);
+    }
+
+    @Test(priority = 3)
+    public void third() throws Exception {
+        test(1);
+    }
+
+    private void test(final Integer expected) {
+        final Response response = target().request().get();
+
+        assertEquals(response.getStatus(), 200);
+        assertEquals(response.readEntity(Integer.class), expected);
+    }
+}
diff --git a/tests/e2e/README b/tests/e2e/README
new file mode 100644
index 0000000..2c2456a
--- /dev/null
+++ b/tests/e2e/README
@@ -0,0 +1,20 @@
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+End-to-end tests module
+=======================
\ No newline at end of file
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml
new file mode 100644
index 0000000..6d9346a
--- /dev/null
+++ b/tests/e2e/pom.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>e2e</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-e2e</name>
+
+    <description>Jersey E2E tests</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-freemarker</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-mustache</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jdk-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-signature</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth2-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>xmlunit</groupId>
+            <artifactId>xmlunit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>xdk</id>
+            <properties>
+                <!-- do not use security manager for xdk -->
+                <surefire.security.argline />
+            </properties>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+    </profiles>
+
+</project>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AbortingFilterTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AbortingFilterTest.java
new file mode 100644
index 0000000..f91c24e
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AbortingFilterTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * JERSEY-2345 Reproducer.
+ * <p/>
+ * If an invalid WWW-Authenticate header is sent from the server and HttpUrlConnector is used,
+ * during processing in the Connector an Exception is thrown (HttpUrlConnection creates an instance of AuthenticateHeader class
+ * which tries to parse the header value during construction. If a scheme is missing (header value is one word),
+ * the constructor throws an exception.
+ * <p/>
+ * This cannot be "fixed" in Jersey.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class AbortingFilterTest extends JerseyTest {
+    private static Logger logger = Logger.getLogger(AbortingFilterTest.class.getName());
+
+    @Path("simple")
+    public static class SimpleTestResource {
+        @GET
+        public Response simpleTest() {
+            return Response.ok().build();
+        }
+
+        @Path("response")
+        @GET
+        public Response simpleResponseTest() {
+            return Response.ok().build();
+        }
+
+        @Path("direct")
+        @GET
+        public Response directResponseTest() {
+            return Response.status(Response.Status.UNAUTHORIZED).header("WWW-Authenticate",
+                    "oauth_problem=token_rejected").build();
+        }
+    }
+
+    public static class AbortingFilter implements ContainerRequestFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext) throws IOException {
+            final Response.ResponseBuilder builder = Response.status(Response.Status.UNAUTHORIZED);
+            builder.header("WWW-Authenticate", "oauth_problem=token_rejected");
+            requestContext.abortWith(builder.build());
+        }
+    }
+
+    public static class AbortingResponseFilter implements ContainerResponseFilter {
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            final Response.ResponseBuilder builder = Response.status(Response.Status.UNAUTHORIZED);
+            builder.header("WWW-Authenticate", "oauth_problem=token_rejected");
+            requestContext.abortWith(builder.build());
+
+        }
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(FilterDynamicBinding.class, SimpleTestResource.class, LoggingFeature.class);
+    }
+
+    /**
+     * Try to abort response in the container response filter. However, it's too late and produces an error.
+     */
+    @Test
+    public void testAbortingResponseFilter() {
+        final Response response = target().path("simple/response").request().get();
+        int status = response.getStatus();
+        logger.info("Response status is: " + status);
+        assertEquals(status, Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+    }
+
+    /**
+     * Try to abort response in a container request filter. With an invalid WWW-Authenticate header (according to HTTP specs)
+     * and HttpUrlConnector, it throws an exception. This is an original scenario reported by jrh3k5 on java.net
+     */
+    @Test(expected = ProcessingException.class)
+    public void testAbortingFilter() {
+        final Response response = target().path("simple").request().get();
+        int status = response.getStatus();
+        logger.info("Response status is: " + status);
+    }
+
+    /**
+     * The original reproted scenario with a different Connector.
+     */
+    @Test
+    public void testAbortingFilterWithApacheConnector() {
+        ClientConfig clientConfig = new ClientConfig();
+        clientConfig.connectorProvider(new ApacheConnectorProvider());
+        Client client = ClientBuilder.newClient(clientConfig);
+
+        final Response response = client.target(getBaseUri()).path("/simple").request().get();
+        int status = response.getStatus();
+        logger.info("Response status is: " + status);
+        assertEquals(status, Response.Status.UNAUTHORIZED.getStatusCode());
+    }
+
+    /**
+     * This test shows, that the behaviour is not caused by the use of the filters, but by the response content itself.
+     */
+    @Test(expected = ProcessingException.class)
+    public void testDirectResponse() {
+        final Response response = target().path("simple/direct").request().get();
+        int status = response.getStatus();
+        logger.info("Response status is: " + status);
+    }
+
+    /**
+     * Dynamically launch specific filters for specific resource methods
+     */
+    public static class FilterDynamicBinding implements DynamicFeature {
+        @Override
+        public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+            if (SimpleTestResource.class.equals(resourceInfo.getResourceClass())) {
+                String methodName = resourceInfo.getResourceMethod().getName();
+                if (methodName.contains("simpleResponseTest")) {
+                    context.register(AbortingResponseFilter.class);
+                    logger.info("Aborting will take place in ResponseFilter.");
+                } else if (methodName.contains("simpleTest")) {
+                    context.register(AbortingFilter.class);
+                    logger.info("Aborting will take place in RequestFilter.");
+                } else {
+                    logger.info("Response will not be aborted.");
+                }
+            }
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AcceptMediaTypeProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AcceptMediaTypeProviderTest.java
new file mode 100644
index 0000000..13ae165
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AcceptMediaTypeProviderTest.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+import org.glassfish.jersey.message.internal.AcceptableMediaType;
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author unknown
+ */
+public class AcceptMediaTypeProviderTest {
+
+    @Test
+    public void testOneMediaType() throws Exception {
+        String header = "application/xml";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(1, l.size());
+
+        MediaType m = l.get(0);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testOneMediaTypeWithParameters() throws Exception {
+        String header = "application/xml;charset=utf8";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(1, l.size());
+
+        MediaType m = l.get(0);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        assertTrue(m.getParameters().containsKey("charset"));
+        assertEquals("utf8", m.getParameters().get("charset"));
+    }
+
+    @Test
+    public void testMultipleMediaType() throws Exception {
+        String header = "application/xml, text/xml, text/html";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testMultipleMediaTypeWithQuality() throws Exception {
+        String header = "application/xml;q=0.1, text/xml;q=0.2, text/html;q=0.3";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testHttpURLConnectionAcceptHeader() throws Exception {
+        String header = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(5, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("image", m.getType());
+        assertEquals("gif", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("image", m.getType());
+        assertEquals("jpeg", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testFirefoxAcceptHeader() throws Exception {
+        String header = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(7, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xhtml+xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("image", m.getType());
+        assertEquals("png", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(5);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(6);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testWithStarAcceptHeader() throws Exception {
+        String header = "application/xml;q=0.1, text/xml;q=0.2, *;q=0.3";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifity() throws Exception {
+        String header = "*/*, text/*, text/plain";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifityWithQuality() throws Exception {
+        String header = "*/*, */*;q=0.5, text/*, text/*;q=0.5, text/plain, text/plain;q=0.5";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        assertEquals(6, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(5);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifityHTTPExample1() throws Exception {
+        String header = "text/*, text/html, text/html;level=1, */*";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifityHTTPExample2() throws Exception {
+        String header = "text/*, text/html;level=1, text/html, */*";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header);
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testHttpURLConnectionAcceptHeaderWithPrority() throws Exception {
+        String header = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
+
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(header,
+                HttpHeaderReader.readQualitySourceMediaType(MediaType.TEXT_HTML));
+
+        assertEquals(5, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("image", m.getType());
+        assertEquals("gif", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("image", m.getType());
+        assertEquals("jpeg", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testFirefoxAcceptHeaderWithPrority() throws Exception {
+        String header = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(
+                header, HttpHeaderReader.readQualitySourceMediaType("text/html;qs=1"));
+
+        assertEquals(7, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("application", m.getType());
+        assertEquals("xhtml+xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("image", m.getType());
+        assertEquals("png", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(5);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(6);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testFirefoxAcceptHeaderWithPrority2() throws Exception {
+        String header = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
+        List<AcceptableMediaType> l = HttpHeaderReader.readAcceptMediaType(
+                header,
+                HttpHeaderReader.readQualitySourceMediaType(new String[]{"text/html;qs=0.8", "application/xml;qs=0.1"}));
+
+        assertEquals(7, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("application", m.getType());
+        assertEquals("xhtml+xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("image", m.getType());
+        assertEquals("png", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(5);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(6);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AnnotationInheritanceTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AnnotationInheritanceTest.java
new file mode 100644
index 0000000..82bb86f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/AnnotationInheritanceTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.Test;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test that JAX-RS annotations are correctly inherited according to the specification.
+ *
+ * The annotations on a super-class should take precedence over annotations on an interface.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class AnnotationInheritanceTest extends JerseyTest {
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(
+                Foo.class, Bar.class
+        );
+    }
+
+    public interface SuperFooable extends Fooable {
+        @GET
+        @Path("superFooableWhatsUp")
+        String getWhatsUp();
+    }
+
+    public interface Fooable {
+        @GET
+        @Path("fooableHello")
+        String getHello();
+
+        @GET
+        @Path("fooableHi")
+        String getHi();
+
+    }
+
+    public abstract static class SuperFoo implements SuperFooable {
+        @GET
+        @Path("superFooHello")
+        public String getHello() {
+            return "-WRONG MESSAGE-";
+        }
+
+        // no annotations here this time
+        public String getHi() {
+            return "-WRONG MESSAGE-";
+        }
+    }
+
+    @Path(value = "/foo")
+    public static class Foo extends SuperFoo implements Fooable {
+        public String getHello() {
+            return "Hello!";
+        }
+
+        public String getHi() {
+            return "Hi!";
+        }
+
+        public String getWhatsUp() {
+            return "What's up?";
+        }
+    }
+
+    @Path("hyperBar")
+    public static class HyperBar {
+
+    }
+
+    public static class SuperBar extends HyperBar {
+    }
+
+    @Path("barable")
+    public interface Barable {
+    }
+
+
+    public static class Bar extends SuperBar implements Barable {
+        @GET
+        @Path("bar")
+        public String getBar() {
+            return "bar";
+        }
+    }
+
+    /**
+     * Test that when there are conflicting annotations on the methods, the annotations in the superclass has higher
+     * priority than the one in the interface.
+     */
+    @Test
+    public void testSuperClassPrecedence() {
+        final String superClassResponse = target().path("foo/superFooHello").request(MediaType.TEXT_PLAIN).get(String.class);
+        assertEquals("The path from the super-class annotation should be used instead of the path from interface",
+                "Hello!", superClassResponse);
+
+        final Response ifaceResponse = target().path("foo/fooableHello").request(MediaType.TEXT_PLAIN).get(Response.class);
+        assertEquals("The path defined in the interface annotation should not exist.", 404, ifaceResponse.getStatus());
+    }
+
+    /**
+     * Test that the annotation is inherited from the interface, if it cannot be found in the chain of superclasses.
+     */
+    @Test
+    public void testInterfaceAnnotationInheritance() {
+        final String response = target().path("foo/fooableHi").request(MediaType.TEXT_PLAIN).get(String.class);
+        assertEquals("The path from the super-class annotation should inherited.",
+                "Hi!", response);
+    }
+
+    /**
+     * Test that if the annotation is not found in the chain of superclasses, it is inherited from the interface, that
+     * is "nearest" ito the class. In this particular case - superclasses do not have annotation,
+     * that could be inherited, neither does the interface directly implemented by the class, so the test expects the
+     * annotation to be inherited from the interface of the superclass.
+     */
+    @Test
+    public void testInheritenceFromSuperclassInterface() {
+        final String response = target().path("foo/superFooableWhatsUp").request(MediaType.TEXT_PLAIN).get(String.class);
+        assertEquals("The path from the interface of the superclass should inherited.",
+                "What's up?", response);
+    }
+
+    /**
+     * Test that class-level annotation behave in the similar manner as the method-level annotations, although this
+     * behaviour is not directly specified in the JSR-339 (in the Chapter 3.6., the specification explicitly states
+     * that "Note that inheritance of class or interface annotations is not supported".
+     *
+     * Jersey does support class-level annotations inheritance as its specific behaviour beyond the JSR scope.
+     */
+    @Test
+    public void testClassAnnotationInheritance() {
+        final String superClassResponse = target().path("hyperBar/bar").request(MediaType.TEXT_PLAIN).get(String.class);
+        assertEquals("The path from the superclass annotation should be used instead of the path from interface",
+                "bar", superClassResponse);
+
+        final Response ifaceResponse = target().path("barable/bar").request(MediaType.TEXT_PLAIN).get(Response.class);
+        assertEquals("The path defined in the interface annotation should not exist.", 404, ifaceResponse.getStatus());
+    }
+
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CacheControlImplTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CacheControlImplTest.java
new file mode 100644
index 0000000..678c965
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CacheControlImplTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import javax.ws.rs.core.CacheControl;
+
+import org.glassfish.jersey.message.internal.CacheControlProvider;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Marc Hadley
+ */
+public class CacheControlImplTest {
+
+    @Test
+    public void testToString() {
+        CacheControlProvider p = new CacheControlProvider();
+        CacheControl instance = new CacheControl();
+
+        instance.setNoCache(true);
+        String expResult = "no-cache, no-transform";
+        String result = p.toString(instance);
+        assertEquals(expResult, result);
+
+        instance.setNoStore(true);
+        expResult = "no-cache, no-store, no-transform";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+
+        instance.setPrivate(true);
+        expResult = "private, no-cache, no-store, no-transform";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+
+        instance.getPrivateFields().add("Fred");
+        expResult = "private=\"Fred\", no-cache, no-store, no-transform";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+        instance.getPrivateFields().add("Bob");
+        expResult = "private=\"Fred, Bob\", no-cache, no-store, no-transform";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+
+        instance = new CacheControl();
+        instance.getCacheExtension().put("key1", "value1");
+        expResult = "no-transform, key1=value1";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+        instance.getCacheExtension().put("key1", "value1 with spaces");
+        expResult = "no-transform, key1=\"value1 with spaces\"";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+
+        instance.setNoStore(true);
+        expResult = "no-store, no-transform, key1=\"value1 with spaces\"";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+
+        instance = new CacheControl();
+        instance.getCacheExtension().put("key1", null);
+        expResult = "no-transform, key1";
+        result = p.toString(instance);
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testRoundTrip() {
+        checkRoundTrip("no-cache, no-transform");
+        checkRoundTrip("no-cache, no-store, no-transform");
+        checkRoundTrip("private, no-cache, no-store, no-transform");
+        checkRoundTrip("private=\"Fred\", no-cache, no-store, no-transform");
+        checkRoundTrip("private=\"Fred, Bob\", no-cache, no-store, no-transform");
+        checkRoundTrip("no-transform, key1=value1");
+        checkRoundTrip("no-transform, key1=\"value1 with spaces\"");
+        checkRoundTrip("no-store, no-transform, key1=\"value1 with spaces\"");
+        checkRoundTrip("no-transform, key1");
+        checkRoundTrip("must-revalidate, proxy-revalidate");
+        checkRoundTrip("max-age=1, s-maxage=1");
+    }
+
+    private void checkRoundTrip(String s) {
+        CacheControlProvider p = new CacheControlProvider();
+
+        CacheControl cc1 = p.fromString(s);
+        CacheControl cc2 = p.fromString(cc1.toString());
+        cc2.toString();
+
+        cc1.equals(cc2);
+
+        try {
+            assertEquals(cc1, cc2);
+        } catch (RuntimeException ex) {
+            throw ex;
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ClientInvocationTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ClientInvocationTest.java
new file mode 100644
index 0000000..200f0ec
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ClientInvocationTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.GenericType;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * {@link Invocation} E2E API tests.
+ *
+ * @author Michal Gajdos
+ */
+public class ClientInvocationTest extends JerseyTest {
+
+    private static final int INVOCATIONS = 5;
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String get() {
+            return "OK";
+        }
+
+        @POST
+        public String post(final String entity) {
+            return entity;
+        }
+    }
+
+    @Test
+    public void testMultipleSyncInvokerCalls() throws Exception {
+        final Invocation.Builder request = target().request();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(request.get().readEntity(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleSyncInvokerCallsAsString() throws Exception {
+        final Invocation.Builder request = target().request();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(request.get(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleSyncInvokerCallsAsGenericType() throws Exception {
+        final Invocation.Builder request = target().request();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(request.get(new GenericType<String>() {}), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleSyncInvokerCallsWithEntity() throws Exception {
+        final Invocation.Builder request = target().request();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final String entity = "Message: " + i;
+            assertThat(request.post(Entity.text(entity)).readEntity(String.class), is(entity));
+        }
+    }
+
+    @Test
+    public void testMultipleSyncInvokerCallsAsStringWithEntity() throws Exception {
+        final Invocation.Builder request = target().request();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final String entity = "Message: " + i;
+            assertThat(request.post(Entity.text(entity), String.class), is(entity));
+        }
+    }
+
+    @Test
+    public void testMultipleSyncInvokerCallsAsGenericTypeWithEntity() throws Exception {
+        final Invocation.Builder request = target().request();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final String entity = "Message: " + i;
+            assertThat(request.post(Entity.text(entity), new GenericType<String>() {}), is(entity));
+        }
+    }
+
+    @Test
+    public void testMultipleAsyncInvokerCalls() throws Exception {
+        final AsyncInvoker request = target().request().async();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(request.get().get().readEntity(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleAsyncInvokerCallsAsString() throws Exception {
+        final AsyncInvoker request = target().request().async();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(request.get(String.class).get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleAsyncInvokerCallsAsGenericType() throws Exception {
+        final AsyncInvoker request = target().request().async();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(request.get(new GenericType<String>() {}).get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleAsyncInvokerCallsWithEntity() throws Exception {
+        final AsyncInvoker request = target().request().async();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final String entity = "Message: " + i;
+            assertThat(request.post(Entity.text(entity)).get().readEntity(String.class), is(entity));
+        }
+    }
+
+    @Test
+    public void testMultipleAsyncInvokerCallsAsStringWithEntity() throws Exception {
+        final AsyncInvoker request = target().request().async();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final String entity = "Message: " + i;
+            assertThat(request.post(Entity.text(entity), String.class).get(), is(entity));
+        }
+    }
+
+    @Test
+    public void testMultipleAsyncInvokerCallsAsGenericTypeWithEntity() throws Exception {
+        final AsyncInvoker request = target().request().async();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final String entity = "Message: " + i;
+            assertThat(request.post(Entity.text(entity), new GenericType<String>() {}).get(), is(entity));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationInvokes() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.invoke().readEntity(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationInvokesAsString() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.invoke(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationInvokesAsGenericType() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.invoke(new GenericType<String>() {}), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationInvokesWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.invoke().readEntity(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationInvokesAsStringWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.invoke(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationInvokesAsGenericTypeWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.invoke(new GenericType<String>() {}), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationSubmits() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.submit().get().readEntity(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationSubmitsAsString() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.submit(String.class).get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationSubmitsAsGenericType() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.submit(new GenericType<String>() {}).get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleCallbackInvocationSubmits() throws Exception {
+        final Invocation invocation = target().request().buildGet();
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final AtomicReference<String> response = new AtomicReference<>();
+
+            invocation.submit(new InvocationCallback<String>() {
+                @Override
+                public void completed(final String s) {
+                    response.set(s);
+                    latch.countDown();
+                }
+
+                @Override
+                public void failed(final Throwable throwable) {
+                    response.set(throwable.getMessage());
+                    latch.countDown();
+                }
+            });
+
+            latch.await(5, TimeUnit.SECONDS);
+            assertThat(response.get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationSubmitsWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.submit().get().readEntity(String.class), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationSubmitsAsStringWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.submit(String.class).get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleInvocationSubmitsAsGenericTypeWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            assertThat(invocation.submit(new GenericType<String>() {}).get(), is("OK"));
+        }
+    }
+
+    @Test
+    public void testMultipleCallbackInvocationSubmitsWithEntity() throws Exception {
+        final Invocation invocation = target().request().buildPost(Entity.text("OK"));
+
+        for (int i = 0; i < INVOCATIONS; i++) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final AtomicReference<String> response = new AtomicReference<>();
+
+            invocation.submit(new InvocationCallback<String>() {
+                @Override
+                public void completed(final String s) {
+                    response.set(s);
+                    latch.countDown();
+                }
+
+                @Override
+                public void failed(final Throwable throwable) {
+                    response.set(throwable.getMessage());
+                    latch.countDown();
+                }
+            });
+
+            latch.await(5, TimeUnit.SECONDS);
+            assertThat(response.get(), is("OK"));
+        }
+    }
+}
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ConstructorSelectionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ConstructorSelectionTest.java
new file mode 100644
index 0000000..eac4d1d
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ConstructorSelectionTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2013, 2018 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.api;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test to verify the proper constructor is selected.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ConstructorSelectionTest extends JerseyTest {
+
+    /**
+     * A resource with multiple constructors.
+     */
+    @Path("resource-test")
+    public static class MultipleConstructorResource {
+        private HttpHeaders headers;
+        private UriInfo info;
+        private Application application;
+        private Request request;
+        private Providers provider;
+
+        public MultipleConstructorResource(){
+        }
+
+        public MultipleConstructorResource(@Context HttpHeaders headers){
+            this.headers = headers;
+        }
+
+
+        public MultipleConstructorResource(@Context HttpHeaders headers,
+                                           @Context UriInfo info){
+            this.headers = headers;
+            this.info = info;
+        }
+
+        public MultipleConstructorResource(@Context HttpHeaders headers,
+                                           @Context UriInfo info,
+                                           @Context Application application){
+            this.application = application;
+            this.headers = headers;
+            this.info = info;
+        }
+
+        public MultipleConstructorResource(@Context HttpHeaders headers,
+                                           @Context UriInfo info,
+                                           @Context Application application,
+                                           @Context Request request){
+            this.application = application;
+            this.headers = headers;
+            this.info = info;
+            this.request = request;
+        }
+
+        protected MultipleConstructorResource(@Context HttpHeaders headers,
+                                              @Context UriInfo info,
+                                              @Context Application application,
+                                              @Context Request request,
+                                              @Context Providers provider){
+            this.application = application;
+            this.headers = headers;
+            this.info = info;
+            this.request = request;
+            this.provider = provider;
+        }
+
+        @GET
+        public Response isUsedConstructorWithMostAttributes(){
+            boolean ok = application != null;
+            ok &= headers != null;
+            ok &= info != null;
+            ok &= request != null;
+            ok &= provider == null;
+            Response.Status status = ok ? Response.Status.OK : Response.Status.INTERNAL_SERVER_ERROR;
+            return Response.status(status).build();
+        }
+    }
+
+    /**
+     * Provider with multiple constructors.
+     */
+    @Provider
+    @Consumes(MediaType.TEXT_PLAIN)
+    public static class StringReader implements MessageBodyReader<String> {
+        private HttpHeaders headers;
+        private UriInfo info;
+        private Application application;
+        private Request request;
+        private Providers providers;
+
+        protected StringReader(@Context HttpHeaders headers, @Context UriInfo info,
+                               @Context Application application, @Context Request request,
+                               @Context Providers providers) {
+            super();
+            this.headers = headers;
+            this.info = info;
+            this.application = application;
+            this.request = request;
+            this.providers = providers;
+        }
+
+        public StringReader(@Context HttpHeaders headers, @Context UriInfo info,
+                            @Context Application application, @Context Request request) {
+            super();
+            this.headers = headers;
+            this.info = info;
+            this.application = application;
+            this.request = request;
+        }
+
+        public StringReader(@Context HttpHeaders headers, @Context UriInfo info,
+                            @Context Application application) {
+            super();
+            this.headers = headers;
+            this.info = info;
+            this.application = application;
+        }
+
+        public StringReader(@Context HttpHeaders headers, @Context UriInfo info) {
+            super();
+            this.headers = headers;
+            this.info = info;
+        }
+
+        public StringReader(@Context HttpHeaders headers) {
+            super();
+            this.headers = headers;
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType,
+                                  Annotation[] annotations, MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public String readFrom(Class<String> type, Type genericType,
+                               Annotation[] annotations, MediaType mediaType,
+                               MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            if (headers == null || info == null || application == null || request == null || providers != null) {
+                return "fail";
+            }
+            return "pass";
+        }
+    }
+
+    @Path("provider-test")
+    public static class ProviderResource {
+
+        @POST
+        public String echo(String entity) {
+            return entity;
+        }
+
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MultipleConstructorResource.class, ProviderResource.class, StringReader.class);
+    }
+
+    /**
+     * JERSEY-1529 reproducer.
+     */
+    @Test
+    public void testResourceConstructorSelection() {
+        final Response response = target("resource-test").request().get();
+
+        assertNotNull("Returned response must not be null.", response);
+        assertEquals("Resource constructor with most arguments has not been selected.", 200, response.getStatus());
+    }
+
+    /**
+     * JERSEY-1712 reproducer.
+     */
+    @Test
+    public void testProviderConstructorSelection() {
+        final Response response = target("provider-test").request().post(Entity.text("echo"));
+
+        assertNotNull("Returned response must not be null.", response);
+        assertEquals(200, response.getStatus());
+        assertEquals("pass", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ConsumeProduceTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ConsumeProduceTest.java
new file mode 100644
index 0000000..cc94667
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ConsumeProduceTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ */
+public class ConsumeProduceTest {
+    @Consumes({"*/*", "a/*", "b/*", "a/b", "c/d"})
+    class ConsumesClass {
+    }
+
+    @Produces ({"*/*", "a/*", "b/*", "a/b", "c/d"})
+    class ProducesClass {
+    }
+
+    @Test
+    public void testConsumes() {
+        final Consumes c = ConsumesClass.class.getAnnotation(Consumes.class);
+        final List<MediaType> l = MediaTypes.createFrom(c);
+        checkMediaTypes(l);
+    }
+
+    @Test
+    public void testProduces() {
+        final Produces p = ProducesClass.class.getAnnotation(Produces.class);
+        final List<MediaType> l = MediaTypes.createFrom(p);
+        checkMediaTypes(l);
+    }
+
+    @Consumes("*/*, a/*, b/*, a/b, c/d")
+    class ConsumesStringClass {
+    }
+
+    @Produces("*/*, a/*, b/*, a/b, c/d")
+    class ProducesStringClass {
+    }
+
+    @Test
+    public void testConsumesString() {
+        final Consumes c = ConsumesStringClass.class.getAnnotation(Consumes.class);
+        final List<MediaType> l = MediaTypes.createFrom(c);
+        checkMediaTypes(l);
+    }
+
+    @Test
+    public void testProducesString() {
+        final Produces p = ProducesStringClass.class.getAnnotation(Produces.class);
+        final List<MediaType> l = MediaTypes.createFrom(p);
+        checkMediaTypes(l);
+    }
+
+    @Consumes({"*/*, a/*", "b/*, a/b", "c/d"})
+    class ConsumesStringsClass {
+    }
+
+    @Produces({"*/*, a/*", "b/*, a/b", "c/d"})
+    class ProducesStringsClass {
+    }
+
+    @Test
+    public void testConsumesStrings() {
+        final Consumes c = ConsumesStringsClass.class.getAnnotation(Consumes.class);
+        final List<MediaType> l = MediaTypes.createFrom(c);
+        checkMediaTypes(l);
+    }
+
+    @Test
+    public void testProducesStrings() {
+        final Produces p = ProducesStringsClass.class.getAnnotation(Produces.class);
+        final List<MediaType> l = MediaTypes.createFrom(p);
+        checkMediaTypes(l);
+    }
+
+
+    void checkMediaTypes(final List<MediaType> l) {
+        assertEquals(5, l.size());
+        assertEquals("a", l.get(0).getType());
+        assertEquals("b", l.get(0).getSubtype());
+        assertEquals("c", l.get(1).getType());
+        assertEquals("d", l.get(1).getSubtype());
+        assertEquals("a", l.get(2).getType());
+        assertEquals("*", l.get(2).getSubtype());
+        assertEquals("b", l.get(3).getType());
+        assertEquals("*", l.get(3).getSubtype());
+        assertEquals("*", l.get(4).getType());
+        assertEquals("*", l.get(4).getSubtype());
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
new file mode 100644
index 0000000..63f2cd2
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ContentDispositionTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.text.ParseException;
+import java.util.Date;
+
+import org.glassfish.jersey.media.multipart.ContentDisposition;
+import org.glassfish.jersey.message.internal.HttpDateFormat;
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Imran@SmartITEngineering.Com
+ */
+public class ContentDispositionTest {
+
+    protected String contentDispositionType;
+
+    public ContentDispositionTest() {
+        contentDispositionType = "inline";
+    }
+
+    @Test
+    public void testCreate() {
+        ContentDisposition contentDisposition = ContentDisposition.type(null).build();
+
+        assertNotNull(contentDisposition);
+        assertEquals(null, contentDisposition.getType());
+
+        contentDisposition = ContentDisposition.type(contentDispositionType).build();
+
+        assertNotNull(contentDisposition);
+        assertEquals(contentDispositionType, contentDisposition.getType());
+
+        final Date date = new Date();
+        contentDisposition = ContentDisposition.type(contentDispositionType).fileName("test.file")
+                .creationDate(date).modificationDate(date).readDate(date).size(1222).build();
+
+        assertContentDisposition(contentDisposition, date);
+        String header = contentDispositionType;
+
+        try {
+            contentDisposition = new ContentDisposition(contentDisposition.toString());
+            assertNotNull(contentDisposition);
+            contentDisposition = new ContentDisposition(header);
+            assertNotNull(contentDisposition);
+            assertEquals(contentDispositionType, contentDisposition.getType());
+            final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+            header = contentDispositionType + ";filename=\"test.file\";creation-date=\""
+                    + dateString + "\";modification-date=\"" + dateString + "\";read-date=\""
+                    + dateString + "\";size=1222";
+
+            contentDisposition = new ContentDisposition(header);
+            assertContentDisposition(contentDisposition, date);
+            contentDisposition = new ContentDisposition(HttpHeaderReader.newInstance(header), true);
+            assertContentDisposition(contentDisposition, date);
+        } catch (final ParseException ex) {
+            fail(ex.getMessage());
+        }
+        try {
+            new ContentDisposition((HttpHeaderReader) null, true);
+            fail("NullPointerException was expected to be thrown.");
+        } catch (final ParseException exception) {
+            fail(exception.getMessage());
+        } catch (final NullPointerException exception) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testToString() {
+        final Date date = new Date();
+        final ContentDisposition contentDisposition = ContentDisposition.type(contentDispositionType).fileName("test.file")
+                .creationDate(date).modificationDate(date).readDate(date).size(1222).build();
+        final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+        final String header = contentDispositionType + "; filename=\"test.file\"; creation-date=\""
+                + dateString + "\"; modification-date=\"" + dateString + "\"; read-date=\"" + dateString + "\"; size=1222";
+        assertEquals(header, contentDisposition.toString());
+    }
+
+    protected void assertContentDisposition(final ContentDisposition contentDisposition, Date date) {
+        assertNotNull(contentDisposition);
+        assertEquals(contentDispositionType, contentDisposition.getType());
+        assertEquals("test.file", contentDisposition.getFileName());
+        assertEquals(date.toString(), contentDisposition.getModificationDate().toString());
+        assertEquals(date.toString(), contentDisposition.getReadDate().toString());
+        assertEquals(date.toString(), contentDisposition.getCreationDate().toString());
+        assertEquals(1222, contentDisposition.getSize());
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
new file mode 100644
index 0000000..64e1435
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.util.Map;
+
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.NewCookie;
+
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Marc Hadley
+ */
+public class CookieImplTest {
+
+    @Test
+    public void testCookieToString() {
+        Cookie cookie = new Cookie("fred", "flintstone");
+        String expResult = "$Version=1;fred=flintstone";
+        assertEquals(expResult, cookie.toString());
+
+        cookie = new Cookie("fred", "flintstone", "/path", null);
+        expResult = "$Version=1;fred=flintstone;$Path=/path";
+        assertEquals(expResult, cookie.toString());
+
+        cookie = new Cookie("fred", "flintstone", "/path", ".sun.com");
+        expResult = "$Version=1;fred=flintstone;$Domain=.sun.com;$Path=/path";
+        assertEquals(expResult, cookie.toString());
+
+        cookie = new Cookie("fred", "flintstone", "/path", ".sun.com", 2);
+        expResult = "$Version=2;fred=flintstone;$Domain=.sun.com;$Path=/path";
+        assertEquals(expResult, cookie.toString());
+    }
+
+    @Test
+    public void testCookieValueOf() {
+        Cookie cookie = Cookie.valueOf("$Version=2;fred=flintstone");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals(2, cookie.getVersion());
+
+        cookie = Cookie.valueOf("$Version=1;fred=flintstone;$Path=/path");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals(1, cookie.getVersion());
+        assertEquals("/path", cookie.getPath());
+
+        cookie = Cookie.valueOf("$Version=1;fred=flintstone;$Domain=.sun.com;$Path=/path");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals(1, cookie.getVersion());
+        assertEquals(".sun.com", cookie.getDomain());
+        assertEquals("/path", cookie.getPath());
+    }
+
+    @Test
+    public void testCreateCookies() {
+        String cookieHeader = "fred=flintstone";
+        Map<String, Cookie> cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 1);
+        Cookie c = cookies.get("fred");
+        assertEquals(c.getVersion(), 0);
+        assertTrue("fred".equals(c.getName()));
+        assertTrue("flintstone".equals(c.getValue()));
+
+        cookieHeader = "fred=flintstone,barney=rubble";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 2);
+        c = cookies.get("fred");
+        assertEquals(c.getVersion(), 0);
+        assertTrue("fred".equals(c.getName()));
+        assertTrue("flintstone".equals(c.getValue()));
+        c = cookies.get("barney");
+        assertEquals(c.getVersion(), 0);
+        assertTrue("barney".equals(c.getName()));
+        assertTrue("rubble".equals(c.getValue()));
+
+        cookieHeader = "fred=flintstone;barney=rubble";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 2);
+        c = cookies.get("fred");
+        assertEquals(c.getVersion(), 0);
+        assertTrue("fred".equals(c.getName()));
+        assertTrue("flintstone".equals(c.getValue()));
+        c = cookies.get("barney");
+        assertEquals(c.getVersion(), 0);
+        assertTrue("barney".equals(c.getName()));
+        assertTrue("rubble".equals(c.getValue()));
+
+        cookieHeader = "$Version=1;fred=flintstone;$Path=/path;barney=rubble";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 2);
+        c = cookies.get("fred");
+        assertEquals(c.getVersion(), 1);
+        assertTrue("fred".equals(c.getName()));
+        assertTrue("flintstone".equals(c.getValue()));
+        assertTrue("/path".equals(c.getPath()));
+        c = cookies.get("barney");
+        assertEquals(c.getVersion(), 1);
+        assertTrue("barney".equals(c.getName()));
+        assertTrue("rubble".equals(c.getValue()));
+
+        cookieHeader = "$Version=1;fred=flintstone;$Path=/path,barney=rubble;$Domain=.sun.com";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 2);
+        c = cookies.get("fred");
+        assertEquals(c.getVersion(), 1);
+        assertTrue("fred".equals(c.getName()));
+        assertTrue("flintstone".equals(c.getValue()));
+        assertTrue("/path".equals(c.getPath()));
+        c = cookies.get("barney");
+        assertEquals(c.getVersion(), 1);
+        assertTrue("barney".equals(c.getName()));
+        assertTrue("rubble".equals(c.getValue()));
+        assertTrue(".sun.com".equals(c.getDomain()));
+
+        cookieHeader = "$Version=1; fred = flintstone ; $Path=/path, barney=rubble ;$Domain=.sun.com";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 2);
+        c = cookies.get("fred");
+        assertEquals(c.getVersion(), 1);
+        assertTrue("fred".equals(c.getName()));
+        assertTrue("flintstone".equals(c.getValue()));
+        assertTrue("/path".equals(c.getPath()));
+        c = cookies.get("barney");
+        assertEquals(c.getVersion(), 1);
+        assertTrue("barney".equals(c.getName()));
+        assertTrue("rubble".equals(c.getValue()));
+        assertTrue(".sun.com".equals(c.getDomain()));
+    }
+
+    @Test
+    public void testNewCookieToString() {
+        NewCookie cookie = new NewCookie("fred", "flintstone");
+        String expResult = "fred=flintstone;Version=1";
+        assertEquals(expResult, cookie.toString());
+
+        cookie = new NewCookie("fred", "flintstone", null, null, null, 60, false);
+        expResult = "fred=flintstone;Version=1;Max-Age=60";
+        assertEquals(expResult, cookie.toString());
+
+        cookie = new NewCookie("fred", "flintstone", null, null, "a modern stonage family", 60, false);
+        expResult = "fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60";
+        assertEquals(expResult, cookie.toString());
+    }
+
+    @Test
+    public void testNewCookieValueOf() {
+        NewCookie cookie = NewCookie.valueOf("fred=flintstone;Version=2");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals(2, cookie.getVersion());
+
+        cookie = NewCookie.valueOf("fred=flintstone;Version=1;Max-Age=60");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals(1, cookie.getVersion());
+        assertEquals(60, cookie.getMaxAge());
+
+        cookie = NewCookie.valueOf("fred=flintstone;Version=1;Comment=\"a modern stonage family\";Max-Age=60;Secure");
+        assertEquals("fred", cookie.getName());
+        assertEquals("flintstone", cookie.getValue());
+        assertEquals("a modern stonage family", cookie.getComment());
+        assertEquals(1, cookie.getVersion());
+        assertEquals(60, cookie.getMaxAge());
+        assertTrue(cookie.isSecure());
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/EntityTagProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/EntityTagProviderTest.java
new file mode 100644
index 0000000..dc6f80b
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/EntityTagProviderTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import javax.ws.rs.core.EntityTag;
+
+import org.glassfish.jersey.message.internal.EntityTagProvider;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Marc Hadley
+ */
+public class EntityTagProviderTest {
+
+    @Test
+    public void testToStringWeak() {
+        checkToString(new EntityTag("Hello \"World\"", true), "W/\"Hello \\\"World\\\"\"");
+    }
+
+    @Test
+    public void testToStringStrong() {
+        checkToString(new EntityTag("Hello \"World\""), "\"Hello \\\"World\\\"\"");
+    }
+
+    @Test
+    public void testFromStringWeak() throws Exception {
+        checkFromString("W/\"Hello \\\"World\\\"\"", new EntityTag("Hello \"World\"", true));
+    }
+
+    @Test
+    public void testFromStringStrong() throws Exception {
+        checkFromString("\"Hello \\\"World\\\"\"", new EntityTag("Hello \"World\""));
+    }
+
+    private void checkToString(final EntityTag e, final String result) {
+        final EntityTagProvider instance = new EntityTagProvider();
+        assertEquals(result, instance.toString(e));
+    }
+
+    public void checkFromString(final String e, final EntityTag result) throws Exception {
+        final EntityTagProvider instance = new EntityTagProvider();
+        assertEquals(result, instance.fromString(e));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ExceptionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ExceptionTest.java
new file mode 100644
index 0000000..2908b8f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ExceptionTest.java
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2010, 2018 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.api;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.NotAcceptableException;
+import javax.ws.rs.NotAllowedException;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.NotSupportedException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.RedirectionException;
+import javax.ws.rs.ServerErrorException;
+import javax.ws.rs.ServiceUnavailableException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+/**
+ * Exception throwing and handling related tests; such as
+ * {@link WebApplicationException} handling on both server and client side,
+ * proper exception throwing etc.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ExceptionTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                ExceptionDrivenResource.class,
+                ResponseDrivenResource.class,
+                // JERSEY-1532
+                NonWaeThrowingConverter.class,
+                ParamConverterThrowingNonWaeFieldTestResource1.class,
+                ParamConverterThrowingNonWaeFieldTestResource2.class,
+                ParamConverterThrowingNonWaeFieldTestResource3.class,
+                ParamConverterThrowingNonWaeFieldTestResource4.class,
+                ParamConverterThrowingNonWaeFieldTestResource5.class,
+                ParamConverterThrowingNonWaeFieldTestResource6.class,
+                ParamConverterThrowingNonWaeFieldTestResource7.class,
+                ParamConverterThrowingNonWaeFieldTestResource8.class,
+                ParamConverterThrowingNonWaeFieldTestResource9.class,
+                ParamConverterThrowingNonWaeFieldTestResource10.class,
+                ParamConverterThrowingNonWaeMethodTestResource.class,
+                WaeThrowingConverter.class,
+                ParamConverterThrowingWaeFieldTestResource1.class,
+                ParamConverterThrowingWaeFieldTestResource2.class,
+                ParamConverterThrowingWaeFieldTestResource3.class,
+                ParamConverterThrowingWaeFieldTestResource4.class,
+                ParamConverterThrowingWaeFieldTestResource5.class,
+                ParamConverterThrowingWaeFieldTestResource6.class,
+                ParamConverterThrowingWaeFieldTestResource7.class,
+                ParamConverterThrowingWaeFieldTestResource8.class,
+                ParamConverterThrowingWaeFieldTestResource9.class,
+                ParamConverterThrowingWaeFieldTestResource10.class,
+                ParamConverterThrowingWaeMethodTestResource.class
+        );
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+    }
+
+    static final URI testUri = UriBuilder.fromUri("http://jersey.java.net").build();
+
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    static Map<String, WebApplicationException> ExceptionMAP = new HashMap<String, WebApplicationException>() {{
+        put("301", new RedirectionException(Response.Status.MOVED_PERMANENTLY, testUri));
+        put("302", new RedirectionException(Response.Status.FOUND, testUri));
+        put("303", new RedirectionException(Response.Status.SEE_OTHER, testUri));
+        put("307", new RedirectionException(Response.Status.TEMPORARY_REDIRECT, testUri));
+        put("400", new BadRequestException());
+        put("401", new NotAuthorizedException("challenge"));
+        put("402", new ClientErrorException(402));
+        put("404", new NotFoundException());
+        put("405", new NotAllowedException("OPTIONS"));
+        put("406", new NotAcceptableException());
+        put("415", new NotSupportedException());
+        put("500", new InternalServerErrorException());
+        put("501", new ServerErrorException(501));
+        put("503", new ServiceUnavailableException());
+    }};
+
+    static Map<String, Response> ResponseMAP = new HashMap<String, Response>() {{
+        put("301", Response.status(301).location(testUri).build());
+        put("302", Response.status(302).location(testUri).build());
+        put("303", Response.seeOther(testUri).build());
+        put("307", Response.temporaryRedirect(testUri).build());
+        put("400", Response.status(400).build());
+        put("401", Response.status(401).build());
+        put("402", Response.status(402).build());
+        put("404", Response.status(404).build());
+        put("405", Response.status(405).allow("OPTIONS").build());
+        put("406", Response.status(406).build());
+        put("415", Response.status(415).build());
+        put("500", Response.serverError().build());
+        put("501", Response.status(501).build());
+        put("503", Response.status(503).build());
+    }};
+
+    @Path("exceptionDriven")
+    public static class ExceptionDrivenResource {
+
+        @GET
+        @Path("{status}")
+        public String get(@PathParam("status") String status) {
+            throw ExceptionMAP.get(status);
+        }
+    }
+
+    @Path("responseDriven")
+    public static class ResponseDrivenResource {
+
+        @GET
+        @Path("{status}")
+        public Response get(@PathParam("status") String status) {
+            return ResponseMAP.get(status);
+        }
+    }
+
+    private void _testStatusCode(final String status) {
+        _testStatusCodeViaException("exceptionDriven", status);
+        _testStatusCodeDirectly("exceptionDriven", status);
+        _testStatusCodeViaException("responseDriven", status);
+        _testStatusCodeDirectly("responseDriven", status);
+    }
+
+    private void _testStatusCodeViaException(final String prefix, final String status) {
+
+        final int statusCode = Integer.parseInt(status);
+
+        try {
+
+            target().path(prefix).path(status).request().get(ClientResponse.class);
+            fail("An exception expected");
+        } catch (WebApplicationException ex) {
+            //noinspection ThrowableResultOfMethodCallIgnored
+            assertEquals(ExceptionMAP.get(status).getClass(), ex.getClass());
+
+            final Response response = ex.getResponse();
+            assertEquals(statusCode, response.getStatus());
+
+            if (is3xxCode(statusCode)) {
+                assertNotNull(response.getLocation());
+            }
+        }
+    }
+
+    private void _testStatusCodeDirectly(final String prefix, final String status) {
+        final int statusCode = Integer.parseInt(status);
+        final Response response = target().path(prefix).path(status).request().get();
+        assertEquals(statusCode, response.getStatus());
+        if (is3xxCode(statusCode)) {
+            assertNotNull(response.getLocation());
+        }
+    }
+
+    @Test
+    public void testAllStatusCodes() {
+        for (String status : ExceptionMAP.keySet()) {
+            _testStatusCode(status);
+        }
+    }
+
+    private boolean is3xxCode(final int statusCode) {
+        return 299 < statusCode && statusCode < 400;
+    }
+
+    /**
+     * BEGIN: JERSEY-1532 reproducer code:
+     *
+     * From JAX-RS 2.0 spec, sect. 3.2:
+     * ================================
+     * A WebApplicationException thrown during construction of field or property values using 3 or 4 above
+     * is processed directly as described in Section 3.3.4. Other exceptions thrown during construction of
+     * field or property values using 3 or 4 above are treated as client errors: if the field or property is
+     * annotated with @MatrixParam, @QueryParam or @PathParam then an implementation MUST generate an instance
+     * of NotFoundException (404 status) that wraps the thrown exception and no entity; if the field or property
+     * is annotated with @HeaderParam or @CookieParam then an implementation MUST generate an instance of
+     * BadRequestException (400 status) that wraps the thrown exception and no entity. Exceptions MUST be
+     * processed as described in Section 3.3.4.
+     */
+    public static class NonWaeType {
+    }
+
+    public static class NonWaeException extends RuntimeException {
+    }
+
+    @ParamConverter.Lazy
+    public static class NonWaeThrowingConverter implements ParamConverter<NonWaeType>, ParamConverterProvider {
+
+        @Override
+        public NonWaeType fromString(String value) {
+            throw new NonWaeException();
+        }
+
+        @Override
+        public String toString(NonWaeType value) {
+            throw new NonWaeException();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            if (NonWaeType.class.isAssignableFrom(rawType)) {
+                return (ParamConverter<T>) this;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    @Path("param-converter/non-wae/field/default-matrix")
+    public static class ParamConverterThrowingNonWaeFieldTestResource1 {
+        @DefaultValue("value")
+        @MatrixParam("missing")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/default-path")
+    public static class ParamConverterThrowingNonWaeFieldTestResource2 {
+        @DefaultValue("value")
+        @PathParam("missing")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/default-query")
+    public static class ParamConverterThrowingNonWaeFieldTestResource3 {
+        @DefaultValue("value")
+        @QueryParam("missing")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/default-header")
+    public static class ParamConverterThrowingNonWaeFieldTestResource4 {
+        @DefaultValue("value")
+        @HeaderParam("missing")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/default-cookie")
+    public static class ParamConverterThrowingNonWaeFieldTestResource5 {
+        @DefaultValue("value")
+        @CookieParam("missing")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/matrix")
+    public static class ParamConverterThrowingNonWaeFieldTestResource6 {
+        @MatrixParam("test")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/path/{test}")
+    public static class ParamConverterThrowingNonWaeFieldTestResource7 {
+        @PathParam("test")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/query")
+    public static class ParamConverterThrowingNonWaeFieldTestResource8 {
+        @QueryParam("test")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/header")
+    public static class ParamConverterThrowingNonWaeFieldTestResource9 {
+        @HeaderParam("test")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/field/cookie")
+    public static class ParamConverterThrowingNonWaeFieldTestResource10 {
+        @CookieParam("test")
+        private NonWaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/non-wae/method")
+    public static class ParamConverterThrowingNonWaeMethodTestResource {
+
+        @GET
+        @Path("default-matrix")
+        public Response defaultMatrixTest(@DefaultValue("value") @MatrixParam("missing") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-path")
+        public Response defaultPathTest(@DefaultValue("value") @PathParam("missing") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-query")
+        public Response defaultQueryTest(@DefaultValue("value") @QueryParam("missing") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-header")
+        public Response defaultHeaderTest(@DefaultValue("value") @HeaderParam("missing") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-cookie")
+        public Response defaultCookieTest(@DefaultValue("value") @CookieParam("missing") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("matrix")
+        public Response matrixTest(@MatrixParam("test") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("path/{test}")
+        public Response pathTest(@PathParam("test") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("query")
+        public Response queryTest(@QueryParam("test") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("header")
+        public Response headerTest(@HeaderParam("test") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("cookie")
+        public Response cookieTest(@CookieParam("test") NonWaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Test
+    public void testNonWaeExceptionThrownFromParamConverter() {
+        final WebTarget target = target("param-converter/non-wae/");
+
+        testSingle("field-default-matrix", 404, target.path("field/default-matrix").request());
+        testSingle("field-default-path", 404, target.path("field/default-path").request());
+        testSingle("field-default-query", 404, target.path("field/default-query").request());
+        testSingle("field-default-header", 400, target.path("field/default-header").request());
+        testSingle("field-default-cookie", 400, target.path("field/default-cookie").request());
+
+        testSingle("field-matrix", 404, target.path("field/matrix;test=value").request());
+        testSingle("field-path", 404, target.path("field/path/value").request());
+        testSingle("field-query", 404, target.path("field/query").queryParam("test", "value").request());
+        testSingle("field-header", 400, target.path("field/header").request().header("test", "value"));
+        testSingle("field-cookie", 400, target.path("field/cookie").request().cookie("test", "value"));
+
+        testSingle("method-default-matrix", 404, target.path("method/default-matrix").request());
+        testSingle("method-default-path", 404, target.path("method/default-path").request());
+        testSingle("method-default-query", 404, target.path("method/default-query").request());
+        testSingle("method-default-header", 400, target.path("method/default-header").request());
+        testSingle("method-default-cookie", 400, target.path("method/default-cookie").request());
+
+        testSingle("method-matrix", 404, target.path("method/matrix;test=value").request());
+        testSingle("method-path", 404, target.path("method/path/value").request());
+        testSingle("method-query", 404, target.path("method/query").queryParam("test", "value").request());
+        testSingle("method-header", 400, target.path("method/header").request().header("test", "value"));
+        testSingle("method-cookie", 400, target.path("method/cookie").request().cookie("test", "value"));
+    }
+
+    public static class WaeType {
+    }
+
+    @ParamConverter.Lazy
+    public static class WaeThrowingConverter implements ParamConverter<WaeType>, ParamConverterProvider {
+
+        @Override
+        public WaeType fromString(String value) {
+            throw new WebApplicationException(555);
+        }
+
+        @Override
+        public String toString(WaeType value) {
+            throw new WebApplicationException(555);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            if (WaeType.class.isAssignableFrom(rawType)) {
+                return (ParamConverter<T>) this;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    @Path("param-converter/wae/field/default-matrix")
+    public static class ParamConverterThrowingWaeFieldTestResource1 {
+        @DefaultValue("value")
+        @MatrixParam("missing")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/default-path")
+    public static class ParamConverterThrowingWaeFieldTestResource2 {
+        @DefaultValue("value")
+        @PathParam("missing")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/default-query")
+    public static class ParamConverterThrowingWaeFieldTestResource3 {
+        @DefaultValue("value")
+        @QueryParam("missing")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/default-header")
+    public static class ParamConverterThrowingWaeFieldTestResource4 {
+        @DefaultValue("value")
+        @HeaderParam("missing")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/default-cookie")
+    public static class ParamConverterThrowingWaeFieldTestResource5 {
+        @DefaultValue("value")
+        @CookieParam("missing")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/matrix")
+    public static class ParamConverterThrowingWaeFieldTestResource6 {
+        @MatrixParam("test")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/path/{test}")
+    public static class ParamConverterThrowingWaeFieldTestResource7 {
+        @PathParam("test")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/query")
+    public static class ParamConverterThrowingWaeFieldTestResource8 {
+        @QueryParam("test")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/header")
+    public static class ParamConverterThrowingWaeFieldTestResource9 {
+        @HeaderParam("test")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/field/cookie")
+    public static class ParamConverterThrowingWaeFieldTestResource10 {
+        @CookieParam("test")
+        private WaeType field;
+
+        @GET
+        public Response get() {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Path("param-converter/wae/method")
+    public static class ParamConverterThrowingWaeMethodTestResource {
+
+        @GET
+        @Path("default-matrix")
+        public Response defaultMatrixTest(@DefaultValue("value") @MatrixParam("missing") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-path")
+        public Response defaultPathTest(@DefaultValue("value") @PathParam("missing") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-query")
+        public Response defaultQueryTest(@DefaultValue("value") @QueryParam("missing") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-header")
+        public Response defaultHeaderTest(@DefaultValue("value") @HeaderParam("missing") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("default-cookie")
+        public Response defaultCookieTest(@DefaultValue("value") @CookieParam("missing") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("matrix")
+        public Response matrixTest(@MatrixParam("test") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("path/{test}")
+        public Response pathTest(@PathParam("test") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("query")
+        public Response queryTest(@QueryParam("test") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("header")
+        public Response headerTest(@HeaderParam("test") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+
+        @GET
+        @Path("cookie")
+        public Response cookieTest(@CookieParam("test") WaeType param) {
+            return Response.status(599).entity("This method should not be invoked").build();
+        }
+    }
+
+    @Test
+    public void testWaeExceptionThrownFromParamConverter() {
+        final WebTarget target = target("param-converter/wae/");
+
+        testSingle("field-default-matrix", 555, target.path("field/default-matrix").request());
+        testSingle("field-default-path", 555, target.path("field/default-path").request());
+        testSingle("field-default-query", 555, target.path("field/default-query").request());
+        testSingle("field-default-header", 555, target.path("field/default-header").request());
+        testSingle("field-default-cookie", 555, target.path("field/default-cookie").request());
+
+        testSingle("field-matrix", 555, target.path("field/matrix;test=value").request());
+        testSingle("field-path", 555, target.path("field/path/value").request());
+        testSingle("field-query", 555, target.path("field/query").queryParam("test", "value").request());
+        testSingle("field-header", 555, target.path("field/header").request().header("test", "value"));
+        testSingle("field-cookie", 555, target.path("field/cookie").request().cookie("test", "value"));
+
+        testSingle("method-default-matrix", 555, target.path("method/default-matrix").request());
+        testSingle("method-default-path", 555, target.path("method/default-path").request());
+        testSingle("method-default-query", 555, target.path("method/default-query").request());
+        testSingle("method-default-header", 555, target.path("method/default-header").request());
+        testSingle("method-default-cookie", 555, target.path("method/default-cookie").request());
+
+        testSingle("method-matrix", 555, target.path("method/matrix;test=value").request());
+        testSingle("method-path", 555, target.path("method/path/value").request());
+        testSingle("method-query", 555, target.path("method/query").queryParam("test", "value").request());
+        testSingle("method-header", 555, target.path("method/header").request().header("test", "value"));
+        testSingle("method-cookie", 555, target.path("method/cookie").request().cookie("test", "value"));
+    }
+
+    private void testSingle(String caseName, int expectedStatus, Invocation.Builder request) {
+        final Response response = request.get();
+        assertEquals("Test of an exception thrown during field/parameter injection [" + caseName + "] failed.",
+                expectedStatus,
+                response.getStatus());
+    }
+    /**
+     * END: JERSEY-1532 reproducer code.
+     */
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
new file mode 100644
index 0000000..9d74547
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/FormDataContentDispositionTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.text.ParseException;
+import java.util.Date;
+
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.message.internal.HttpDateFormat;
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Imran@SmartITEngineering.Com
+ */
+public class FormDataContentDispositionTest extends ContentDispositionTest {
+
+    public FormDataContentDispositionTest() {
+        contentDispositionType = "form-data";
+    }
+
+    @Test
+    @Override
+    public void testCreate() {
+        final Date date = new Date();
+        FormDataContentDisposition contentDisposition;
+        contentDisposition = FormDataContentDisposition.name("testData").fileName("test.file").creationDate(date)
+                .modificationDate(date).readDate(date).size(1222).build();
+        assertFormDataContentDisposition(contentDisposition, date);
+        try {
+            final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+            final String header = contentDispositionType + ";filename=\"test.file\";creation-date=\"" + dateString
+                    + "\";modification-date=\"" + dateString + "\";read-date=\"" + dateString + "\";size=1222"
+                    + ";name=\"testData\"";
+
+            contentDisposition = new FormDataContentDisposition(contentDisposition.toString());
+            assertFormDataContentDisposition(contentDisposition, date);
+            contentDisposition = new FormDataContentDisposition(header);
+            assertFormDataContentDisposition(contentDisposition, date);
+            contentDisposition = new FormDataContentDisposition(HttpHeaderReader.newInstance(header), true);
+            assertFormDataContentDisposition(contentDisposition, date);
+        } catch (final ParseException ex) {
+            fail(ex.getMessage());
+        }
+        try {
+            new FormDataContentDisposition((HttpHeaderReader) null, true);
+            fail("NullPointerException was expected to be thrown.");
+        } catch (final ParseException exception) {
+            fail(exception.getMessage());
+        } catch (final NullPointerException exception) {
+            //expected
+        }
+        try {
+            new FormDataContentDisposition("form-data;filename=\"test.file\"");
+            fail("IllegalArgumentException was expected to be thrown.");
+        } catch (final ParseException exception) {
+            fail(exception.getMessage());
+        } catch (final IllegalArgumentException exception) {
+            //expected
+        }
+        try {
+            FormDataContentDisposition.name(null).build();
+            fail("IllegalArgumentException was expected to be thrown.");
+        } catch (final IllegalArgumentException exception) {
+            //expected
+        } catch (final Exception exception) {
+            fail(exception.getMessage());
+        }
+    }
+
+    @Test
+    @Override
+    public void testToString() {
+        final Date date = new Date();
+        final FormDataContentDisposition contentDisposition = FormDataContentDisposition.name("testData")
+                .fileName("test.file").creationDate(date).modificationDate(date)
+                        .readDate(date).size(1222).build();
+        final String dateString = HttpDateFormat.getPreferredDateFormat().format(date);
+        final String header = contentDispositionType + "; filename=\"test.file\"; creation-date=\"" + dateString
+                + "\"; modification-date=\"" + dateString + "\"; read-date=\"" + dateString + "\"; size=1222"
+                + "; name=\"testData\"";
+
+        assertEquals(header, contentDisposition.toString());
+    }
+
+    protected void assertFormDataContentDisposition(final FormDataContentDisposition contentDisposition, final Date date) {
+        assertContentDisposition(contentDisposition, date);
+        assertEquals("testData", contentDisposition.getName());
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
new file mode 100644
index 0000000..b0ecbdc
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/HttpHeaderTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+import org.glassfish.jersey.message.internal.AcceptableLanguageTag;
+import org.glassfish.jersey.message.internal.AcceptableToken;
+import org.glassfish.jersey.message.internal.HttpDateFormat;
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+import org.glassfish.jersey.message.internal.LanguageTag;
+import org.glassfish.jersey.message.internal.ParameterizedHeader;
+import org.glassfish.jersey.message.internal.Token;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ */
+public class HttpHeaderTest {
+
+    @Test
+    public void testTokens() throws ParseException {
+        final String header = "type  /  content; a = \"asdsd\"";
+
+        final HttpHeaderReader r = HttpHeaderReader.newInstance(header);
+        while (r.hasNext()) {
+            r.next();
+        }
+    }
+
+    @Test
+    public void testMediaType() throws ParseException {
+        final String mimeType = "application/xml;charset=UTF-8";
+        MediaType.valueOf(mimeType);
+    }
+
+    @Test
+    public void testLanguageTag() throws ParseException {
+        final String languageTag = "en-US";
+        new LanguageTag(languageTag);
+    }
+
+    @Test
+    public void testAcceptableLanguageTag() throws ParseException {
+        final String languageTag = "en-US;q=0.123";
+        new AcceptableLanguageTag(languageTag);
+    }
+
+    @Test
+    public void testAcceptableLanguageTagList() throws Exception {
+        final String languageTags = "en-US;q=0.123, fr;q=0.2, en;q=0.3, *;q=0.01";
+        final List<AcceptableLanguageTag> l = HttpHeaderReader.readAcceptLanguage(languageTags);
+        assertEquals("en", l.get(0).getTag());
+        assertEquals("fr", l.get(1).getTag());
+        assertEquals("en-US", l.get(2).getTag());
+        assertEquals("*", l.get(3).getTag());
+    }
+
+    @Test
+    public void testToken() throws ParseException {
+        final String token = "gzip";
+        new Token(token);
+    }
+
+    @Test
+    public void testAcceptableToken() throws ParseException {
+        final String token = "gzip;q=0.123";
+        new AcceptableToken(token);
+    }
+
+    @Test
+    public void testAcceptableTokenList() throws Exception {
+        final String tokens = "gzip;q=0.123, compress;q=0.2, zlib;q=0.3, *;q=0.01";
+        final List<AcceptableToken> l = HttpHeaderReader.readAcceptToken(tokens);
+        assertEquals("zlib", l.get(0).getToken());
+        assertEquals("compress", l.get(1).getToken());
+        assertEquals("gzip", l.get(2).getToken());
+        assertEquals("*", l.get(3).getToken());
+    }
+
+    @Test
+    public void testDateParsing() throws ParseException {
+        final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
+        final String date_RFC1036 = "Sunday, 06-Nov-94 08:49:37 GMT";
+        final String date_ANSI_C = "Sun Nov  6 08:49:37 1994";
+
+        HttpHeaderReader.readDate(date_RFC1123);
+        HttpHeaderReader.readDate(date_RFC1036);
+        HttpHeaderReader.readDate(date_ANSI_C);
+    }
+
+    @Test
+    public void testDateFormatting() throws ParseException {
+        final String date_RFC1123 = "Sun, 06 Nov 1994 08:49:37 GMT";
+        final Date date = HttpHeaderReader.readDate(date_RFC1123);
+
+        final String date_formatted = HttpDateFormat.getPreferredDateFormat().format(date);
+        assertEquals(date_RFC1123, date_formatted);
+    }
+
+    @Test
+    public void testParameterizedHeader() throws ParseException {
+        ParameterizedHeader ph = new ParameterizedHeader("a");
+        assertEquals("a", ph.getValue());
+
+        ph = new ParameterizedHeader("a/b");
+        assertEquals("a/b", ph.getValue());
+
+        ph = new ParameterizedHeader("  a  /  b  ");
+        assertEquals("a/b", ph.getValue());
+
+        ph = new ParameterizedHeader("");
+        assertEquals("", ph.getValue());
+
+        ph = new ParameterizedHeader(";");
+        assertEquals("", ph.getValue());
+        assertEquals(0, ph.getParameters().size());
+
+        ph = new ParameterizedHeader(";;;");
+        assertEquals("", ph.getValue());
+        assertEquals(0, ph.getParameters().size());
+
+        ph = new ParameterizedHeader("  ;  ;  ;  ");
+        assertEquals("", ph.getValue());
+        assertEquals(0, ph.getParameters().size());
+
+        ph = new ParameterizedHeader("a;x=1;y=2");
+        assertEquals("a", ph.getValue());
+        assertEquals(2, ph.getParameters().size());
+        assertEquals("1", ph.getParameters().get("x"));
+        assertEquals("2", ph.getParameters().get("y"));
+
+        ph = new ParameterizedHeader("a ;  x=1  ;  y=2  ");
+        assertEquals("a", ph.getValue());
+        assertEquals(2, ph.getParameters().size());
+        assertEquals("1", ph.getParameters().get("x"));
+        assertEquals("2", ph.getParameters().get("y"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocaleProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocaleProviderTest.java
new file mode 100644
index 0000000..9bce717
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocaleProviderTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.util.Locale;
+
+import org.glassfish.jersey.message.internal.LocaleProvider;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Mark Hadley
+ */
+public class LocaleProviderTest {
+    @Test
+    public void testToString() {
+        final LocaleProvider instance = new LocaleProvider();
+        assertEquals("en", instance.toString(new Locale("en")));
+        assertEquals("en-US", instance.toString(new Locale("en", "us")));
+    }
+
+    @Test
+    public void testFromString() throws Exception {
+        final LocaleProvider instance = new LocaleProvider();
+        assertEquals(new Locale("en"), instance.fromString("en"));
+        assertEquals(new Locale("en", "us"), instance.fromString("en-us"));
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderAsyncTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderAsyncTest.java
new file mode 100644
index 0000000..b53f999
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderAsyncTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import java.net.URI;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test if the location relativer URI is correctly resolved within asynchronous processing cases.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RunWith(ConcurrentRunner.class)
+public class LocationHeaderAsyncTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(LocationHeaderAsyncTest.class.getName());
+    static ExecutorService executor;
+
+    private static final AtomicBoolean executorComparisonFailed = new AtomicBoolean(false);
+    private static final AtomicBoolean interrupted = new AtomicBoolean(false);
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(ResponseTest.class);
+    }
+
+    /**
+     * Prepare test infrastructure.
+     *
+     * In this case it prepares executor thread pool of size one and initializes the thread.
+     * @throws Exception
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        /* thread pool for custom executor async test */
+        LocationHeaderAsyncTest.executor = Executors.newFixedThreadPool(1);
+
+        // Force the thread to be eagerly instantiated - this prevents the instantiation later and ensures, that the thread
+        // will not be a child thread of the request handling thread, so the thread-local baseUri variable will not be inherited.
+        LocationHeaderAsyncTest.executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                LOGGER.info("Thread pool initialized.");
+            }
+        });
+    }
+
+    /**
+     * Test JAX-RS resource
+     */
+    @SuppressWarnings("VoidMethodAnnotatedWithGET")
+    @Path(value = "/ResponseTest")
+    public static class ResponseTest {
+
+        /* injected request URI for assertions in the resource methods */
+        @Context
+        private UriInfo uriInfo;
+
+        /**
+         * Asynchronous resource method for testing if the URI is absolutized also in case of asynchronous processing;
+         *
+         * The response is created in the separate thread. This tests, that the thread still has access to the request baseUri
+         * thread-local variable in {@link org.glassfish.jersey.message.internal.OutboundJaxrsResponse.Builder}.
+         */
+        @GET
+        @Path("locationAsync")
+        public void locationAsync(@Suspended final AsyncResponse asyncResponse) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    final URI uri = getUriBuilder().segment("locationAsync").build();
+                    final Response result = Response.created(uri).build();
+
+                    final URI location = result.getLocation();
+                    if (uriInfo.getAbsolutePath().equals(location)) {
+                        asyncResponse.resume(result);
+                    } else {
+                        asyncResponse.resume(Response.serverError().entity(location.toString()).build());
+                    }
+
+                }
+            }).start();
+        }
+
+        /**
+         * Resource method for async test with custom executor.
+         *
+         * It runs in a thread that was not created within the request scope, so it does not inherit the baseUri thread-local
+         * variable value.
+         * In this case, URI will not be absolutized until calling {@link AsyncResponse#resume(Object)}.
+         */
+        @GET
+        @Path("executorAsync")
+        @ManagedAsync
+        public void executorAsync(@Suspended final AsyncResponse asyncResponse) {
+            LocationHeaderAsyncTest.executor.submit(new Runnable() {
+                @Override
+                public void run() {
+                    final URI uri = getUriBuilder().segment("executorAsync").build();
+                    final Response result = Response.created(uri).build();
+                    asyncResponse.resume(result);
+                    if (!uriInfo.getAbsolutePath().equals(result.getLocation())) {
+                        executorComparisonFailed.set(true);
+                    }
+                }
+            });
+        }
+
+        /**
+         * Placeholder for the suspended async responses;
+         * For the current test a simple static field would be enough, but this is easily extensible;
+         *
+         * This is inspired by the {@link AsyncResponse} javadoc example
+         */
+        private static final BlockingQueue<AsyncResponse> suspended = new ArrayBlockingQueue<>(5);
+
+        /**
+         * Start of the async test - stores the asynchronous response object
+         */
+        @GET
+        @Path("locationAsyncStart")
+        public void locationAsyncStart(@Suspended final AsyncResponse asyncResponse) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        suspended.put(asyncResponse);
+                    } catch (final InterruptedException e) {
+                        asyncResponse.cancel();
+                        Thread.currentThread().interrupt();
+                        interrupted.set(true);
+                    }
+                }
+            }).start();
+        }
+
+        /**
+         * Finish of the async test - creates a response, checks the location header and resumes the asyncResponse
+         * @return true if the URI was correctly absolutized, false if the URI is relative or differs from the expected URI
+         */
+        @GET
+        @Path("locationAsyncFinish")
+        public Boolean locationAsyncFinish() throws InterruptedException {
+            final AsyncResponse asyncResponse = suspended.poll(2000, TimeUnit.MILLISECONDS);
+
+            final URI uri = getUriBuilder().segment("locationAsyncFinish").build();
+            final Response result = Response.created(uri).build();
+            final boolean wasEqual = result.getLocation().equals(uriInfo.getAbsolutePath());
+
+            asyncResponse.resume(result);
+            return wasEqual;
+        }
+
+        /** Return UriBuilder with base pre-set {@code /ResponseTest} uri segment for this resource.
+         *
+         * @return UriBuilder
+         */
+        private UriBuilder getUriBuilder() {
+            return UriBuilder.fromResource(ResponseTest.class);
+        }
+    }
+
+    /**
+     * Basic asynchronous testcase; checks if the URI is correctly absolutized also within a separate thread during
+     * async processing
+     */
+    @Test
+    public void testAsync() {
+        final String expectedUri = getBaseUri() + "ResponseTest/locationAsync";
+        final Response response = target().path("ResponseTest/locationAsync").request().get(Response.class);
+
+        final String msg = String.format("Comparison failed in the resource method. \nExpected: %1$s\nActual: %2$s",
+                expectedUri, response.readEntity(String.class));
+        assertNotEquals(msg, response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals(expectedUri, location);
+    }
+
+    /**
+     * Test with a thread from thread-pool (created out of request scope)
+     */
+    @Test
+    public void testExecutorAsync() {
+        final Response response = target().path("ResponseTest/executorAsync").request().get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertFalse("The comparison failed in the resource method.", executorComparisonFailed.get());
+        assertEquals(getBaseUri() + "ResponseTest/executorAsync", location);
+    }
+
+    /**
+     * Asynchronous testcase split over two distinct requests
+     */
+    @Test
+    public void testSeparatedAsync() throws ExecutionException, InterruptedException {
+        final Future<Response> futureResponse = target().path("ResponseTest/locationAsyncStart").request().async().get();
+        final Boolean result = target().path("ResponseTest/locationAsyncFinish").request().get(Boolean.class);
+        assertFalse("Thread was interrupted on inserting into blocking queue.", interrupted.get());
+        assertTrue(result);
+
+        final Response response = futureResponse.get();
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri() + "ResponseTest/locationAsyncFinish", location);
+    }
+}
+
+
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderBasicTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderBasicTest.java
new file mode 100644
index 0000000..767e2e0
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderBasicTest.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import java.net.URI;
+import java.util.logging.Logger;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test if the location relative URI is correctly resolved within basic cases.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RunWith(ConcurrentRunner.class)
+public class LocationHeaderBasicTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(LocationHeaderBasicTest.class.getName());
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(ResponseTest.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        super.configureClient(config);
+        config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+    }
+
+    /**
+     * Test JAX-RS resource
+     */
+    @Path(value = "/ResponseTest")
+    public static class ResponseTest {
+
+        /* injected request URI for assertions in the resource methods */
+        @Context
+        private UriInfo uriInfo;
+
+        /**
+         * Resource method for the basic uri conversion test
+         * @return test response with relative location uri
+         */
+        @GET
+        @Path("location")
+        public Response locationTest() {
+            final URI uri = URI.create("location");
+            LOGGER.info("URI Created in the resource method > " + uri);
+            return Response.created(uri).build();
+        }
+
+        /**
+         * Resource method for the test with null location
+         * @return test response with null location uri
+         */
+        @GET
+        @Path("locationNull")
+        public Response locationTestNull() {
+            return Response.created(null).build();
+        }
+
+        /**
+         * Resource method for the test with entity containing response
+         * @return test response with relative uri and with entity
+         */
+        @GET
+        @Path("locationWithBody")
+        @Produces("text/plain")
+        public Response locationTestWithBody() {
+            final URI uri = URI.create("locationWithBody");
+            return Response.created(uri).entity("Return from locationWithBody").type("text/plain").build();
+        }
+
+        /**
+         * Resource method for direct test - location header is checked immediately after calling Response.created() and
+         * the result is returned as a boolean response instead of returning the ({@link Response}) type and checking the
+         * header in the calling test method. This isolates the influence of absolutization routine performed in the
+         * ({@link org.glassfish.jersey.server.ServerRuntime} before closing the stream.
+         *
+         * @return true if URI is absolutized correctly, false if the URI remains relative (or does not match the expected one).
+         */
+        @GET
+        @Path("locationDirect")
+        @Produces("text/plain")
+        public Boolean locationDirectTest() {
+            final URI uri = getUriBuilder().segment("locationDirect").build();
+            final Response response = Response.created(uri).build();
+            return response.getLocation().equals(uriInfo.getAbsolutePath());
+        }
+
+
+
+
+        /**
+         * Resource method for testing correct baseUri and request overwrite in the prematching filter.
+         * Should never be called by the test, as {@link ResponseTest#redirectedUri()} should be called instead.
+         */
+        @GET
+        @Path("filterChangedBaseUri")
+        public Response locationWithChangedBaseUri() {
+            fail("Method should not expected to be called, as prematching filter should have changed the request uri.");
+            return Response.created(URI.create("new")).build();
+        }
+
+        /**
+         * Not called by the test directly, but after prematching filter redirect from
+         * {@link ResponseTest#locationWithChangedBaseUri()}.
+         *
+         * @return {@code 201 Created} response with location resolved against new baseUri.
+         */
+        @GET
+        @Path("newUri")
+        public Response redirectedUri() {
+            return Response.created(URI.create("newRedirected")).build();
+        }
+
+        /**
+         * Resource method for testing relative URI resolution in case of {@code seeOther} response.
+         * @return {@code 303 See Other} response with relative URI
+         */
+        @POST
+        @Path("seeOther")
+        @Consumes("text/plain")
+        public Response seeOther() {
+            return Response.seeOther(URI.create("other")).build();
+        }
+
+        /**
+         * Resource method for testing relative URI resolution in case of {@code seeOther} response.
+         * @return {@code 303 See Other} response with relative URI
+         */
+        @GET
+        @Path("seeOtherLeading")
+        public Response seeOtherWithLeadingSlash() {
+            return Response.seeOther(URI.create("/other")).build();
+        }
+
+        /**
+         * Resource method for testing relative URI resolution in case of {@code seeOther} response.
+         * @return {@code 303 See Other} response with relative URI
+         */
+        @GET
+        @Path("seeOtherTrailing")
+        public Response seeOtherWithTrailingSlash() {
+            return Response.seeOther(URI.create("other/")).build();
+        }
+
+        /**
+         * Resource method for testing relative URI resolution in case of {@code temporaryRedirect} response.
+         * @return {@code 307 Temporary Redirect} response with relative URI
+         */
+        @GET
+        @Path("temporaryRedirect")
+        public Response temporaryRedirect() {
+            return Response.temporaryRedirect(URI.create("redirect")).build();
+        }
+
+        /**
+         * Resource method for testing relative URI resolution in case of {@code temporaryRedirect} response.
+         * @return {@code 307 Temporary Redirect} response with relative URI
+         */
+        @GET
+        @Path("temporaryRedirectLeading")
+        public Response temporaryRedirectWithLeadingSlash() {
+            return Response.temporaryRedirect(URI.create("/redirect")).build();
+        }
+
+        /**
+         * Resource method for testing relative URI resolution in case of {@code temporaryRedirect} response.
+         * @return {@code 307 Temporary Redirect} response with relative URI
+         */
+        @GET
+        @Path("temporaryRedirectTrailing")
+        public Response temporaryRedirectWithTrailingSlash() {
+            return Response.temporaryRedirect(URI.create("redirect/")).build();
+        }
+
+        /** Return UriBuilder with base pre-set {@code /ResponseTest} uri segment for this resource.
+         *
+         * @return UriBuilder
+         */
+        private UriBuilder getUriBuilder() {
+            return UriBuilder.fromResource(ResponseTest.class);
+        }
+    }
+
+    /**
+     * Basic test; resource methods returns relative uri, test expects uri to be absolute
+     */
+    @Test
+    public void testConvertRelativeUriToAbsolute() {
+        checkResource("ResponseTest/location", "location");
+        // checkResource("ResponseTest/location");
+    }
+
+    /**
+     * Test with entity; most of the HTTP 201 Created responses do not contain any body, just headers.
+     * This test ensures, that the uri conversion works even in case when entity is present.
+     */
+    @Test
+    public void testAbsoluteUriWithEntity() {
+        final Response response = checkResource("ResponseTest/locationWithBody", "locationWithBody");
+        assertNotNull(response.getEntity());
+    }
+
+
+    /**
+     * Test with null location;
+     * Ensures, that the null location is processed correctly.
+     */
+    @Test
+    public void testNullLocation() {
+        final Response response = target().path("ResponseTest/locationNull").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertNull("Location header should be absolute URI", location);
+    }
+
+    /**
+     * Tests if the URI is absolutized in the Response directly after Response.Builder.created() is called
+     */
+    @Test
+    public void testConversionDirectly() {
+        final Boolean result = target().path("ResponseTest/locationDirect").request(MediaType.TEXT_PLAIN).get(Boolean.class);
+        assertTrue(result);
+    }
+
+    @Test
+    public void testSeeOther() {
+        Response response = target().path("ResponseTest/seeOther").request()
+                .post(Entity.entity("TEXT", MediaType.TEXT_PLAIN_TYPE));
+        String location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri().toString() + "other", location);
+
+        response = target().path("ResponseTest/seeOtherLeading").request(MediaType.TEXT_PLAIN).get(Response.class);
+        location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri().toString() + "other", location);
+
+        response = target().path("ResponseTest/seeOtherTrailing").request(MediaType.TEXT_PLAIN).get(Response.class);
+        location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri().toString() + "other/", location);
+    }
+
+    @Test
+    public void testTemporaryRedirect() {
+        Response response = target().path("ResponseTest/temporaryRedirect").request(MediaType.TEXT_PLAIN).get(Response.class);
+        String location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri().toString() + "redirect", location);
+
+        response = target().path("ResponseTest/temporaryRedirectLeading").request(MediaType.TEXT_PLAIN).get(Response.class);
+        location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri().toString() + "redirect", location);
+
+        response = target().path("ResponseTest/temporaryRedirectTrailing").request(MediaType.TEXT_PLAIN).get(Response.class);
+        location = response.getHeaderString(HttpHeaders.LOCATION);
+        assertEquals(getBaseUri().toString() + "redirect/", location);
+    }
+
+    private Response checkResource(final String resourcePath, final String expectedRelativeUri) {
+        final Response response = target().path(resourcePath).request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals(getBaseUri() + expectedRelativeUri, location);
+        return response;
+    }
+}
+
+
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderFiltersTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderFiltersTest.java
new file mode 100644
index 0000000..d6975ac
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderFiltersTest.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.Test;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Logger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test if the location response header relative URI is correctly resolved within complex cases with interceptors, filters,
+ * exception mappers, etc.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class LocationHeaderFiltersTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(LocationHeaderBasicTest.class.getName());
+
+    static ExecutorService executor;
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(
+                MyTest.class,
+                LocationManipulationDynamicBinding.class,
+                AbortingPreMatchingRequestFilter.class,
+                BaseUriChangingPreMatchingFilter.class,
+                TestExceptionMapper.class
+        );
+    }
+
+    /**
+     * Prepare test infrastructure.
+     *
+     * In this case it prepares executor thread pool of size one and initializes the thread.
+     * @throws Exception
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        /* thread pool for custom executor async test */
+        LocationHeaderFiltersTest.executor = Executors.newFixedThreadPool(1);
+
+        // Force the thread to be eagerly instantiated - this prevents the instantiation later and ensures, that the thread
+        // will not be a child thread of the request handling thread, so the thread-local baseUri variable will not be inherited.
+        LocationHeaderFiltersTest.executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                LOGGER.info("Thread pool initialized.");
+            }
+        });
+    }
+
+
+    /**
+     * Test JAX-RS resource
+     */
+    @SuppressWarnings("VoidMethodAnnotatedWithGET")
+    @Path(value = "/ResponseTest")
+    public static class MyTest {
+
+        /* injected request URI for assertions in the resource methods */
+        @Context
+        private UriInfo uriInfo;
+
+        /**
+         * Resource method for the test with uri rewritten in the filter
+         * @return test response with relative location uri
+         */
+        @GET
+        @Path("locationTestWithFilter")
+        public Response locationTestWithFilter() {
+            final URI uri = URI.create("location");
+            LOGGER.info("URI Created in the resource method > " + uri);
+            return Response.created(uri).build();
+        }
+
+        /**
+         * Resource method for the test with uri rewritten in the interceptor
+         * @return test response with relative location uri and with body
+         * (write interceptors are not triggered for entity-less responses)
+         */
+        @GET
+        @Path("locationWithInterceptor")
+        public Response locationTestWithInterceptor() {
+            final URI uri = URI.create("foo");
+            return Response.created(uri).entity("Return from locationTestWithInterceptor").type("text/plain").build();
+        }
+
+
+        /**
+         * Resource method for testing URI absolutization after the abortion in the post-matching filter.
+         * @return dummy response - this string should never be propagated to the client (the processing chain
+         * will be aborted in the filter before this resource method is even called.
+         * However, it is needed here, because the filter is bound to the resource method name.
+         */
+        @GET
+        @Path("locationAborted")
+        public String locationTestAborted() {
+            assertTrue("The resource method locationTestAborted() should not have been called. The post-matching filter was "
+                    + "not configured correctly. ", false);
+            return "DUMMY_RESPONSE"; // this string should never reach the client (the resource method will not be called)
+        }
+
+        /**
+         * Resource method for testing URI absolutization after the abortion in the pre-matching filter.
+         * @return dummy response - this string should never be propagated to the client (the processing chain will be
+         * executorComparisonFailed in the filter before this resource method is even called.
+         * However, it is needed here, because the filter is bound to the resource method name.
+         */
+        @GET
+        @Path("locationAbortedPreMatching")
+        public String locationTestPreMatchingAborted() {
+            assertTrue("The resource method locationTestPreMatchingAborted() should not have been called. The pre-matching "
+                    + "filter was not configured correctly. ", false);
+            return "DUMMY_RESPONSE"; // this string should never reach the client (the resource method will not be called)
+        }
+
+        /**
+         * Resource method for the test of ResponseFilters in the sync case.
+         * Returns response with a relative URI, which is than absolutized by Jersey.
+         * Later the {@link UriCheckingResponseFilter} is triggered and checks the URI again - the check itself is done in the
+         * filter.
+         * Based on the result of the check, the filter returns the original status (201 - Created) or an
+         * error status (500 - Internal Server Error) with an error message in the response body.
+         */
+        @GET
+        @Path("responseFilterSync")
+        public Response responseFilterSync() {
+            return Response.created(URI.create("responseFilterSync")).build();
+        }
+
+        /**
+         * Resource method for the test of ResponseFilters in the async case. It runs in the separate thread created on request.
+         *
+         * Returns response with a relative URI, which is than absolutized by Jersey.
+         * Later the {@link UriCheckingResponseFilter} is triggered and checks the URI again - the check itself is done in the
+         * filter.
+         * Based on the result of the check, the filter returns the original status (201 - Created) or an
+         * error status (500 - Internal Server Error) with an error message in the response body.
+         */
+        @GET
+        @Path("responseFilterAsync")
+        public void responseFilterAsync(@Suspended final AsyncResponse asyncResponse) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    final Response result = Response.created(URI.create("responseFilterAsync")).build();
+                    asyncResponse.resume(result);
+                }
+            }).start();
+        }
+
+        /**
+         * Resource method for the test of ResponseFilters in the async/executor case. It runs in a thread created out of the
+         * request scope.
+         *
+         * Returns response with a relative URI, which is than absolutized by Jersey.
+         * Later the {@link UriCheckingResponseFilter} is triggered and checks the URI again - the check itself is done in the
+         * filter.
+         * Based on the result of the check, the filter returns the original status (201 - Created) or an
+         * error status (500 - Internal Server Error) with an error message in the response body.
+         */
+        @GET
+        @Path("responseFilterAsyncExecutor")
+        public void responseFilterAsyncExecutor(@Suspended final AsyncResponse asyncResponse) {
+            executor.submit(new Runnable() {
+                @Override
+                public void run() {
+                    final Response result =
+                            Response.created(URI.create("responseFilterAsyncExecutor")).build();
+                    asyncResponse.resume(result);
+                }
+            });
+        }
+
+        /**
+         * Resource method for testing for testing the URI absolutization in the exception mapper in the synchronous case.
+         *
+         * Method always throws {@link WebApplicationException}, which is defined to be handled by {@link TestExceptionMapper}.
+         * The exception mapper then creates the response with relative URI and response is routed
+         * into {@link UriCheckingResponseFilter}, which checks if th URI was correctly absolutized.
+         * @return does not return any response
+         */
+        @GET
+        @Path("exceptionMapperSync")
+        public Response exceptionMapperSync() {
+            throw new WebApplicationException();
+        }
+
+        /**
+         * Resource method for testing for testing the URI absolutization in the exception mapper in the asynchronous case.
+         * New thread is started for the resource method processing.
+         *
+         * Method always "throws" {@link WebApplicationException} (in case of async methods,
+         * the exceptions are not thrown directly, but passed to {@link AsyncResponse#resume(Throwable)}),
+         * which is defined to be handled by {@link TestExceptionMapper}.         *
+         * The exception mapper then creates the response with relative URI and response is routed
+         * into {@link UriCheckingResponseFilter}, which checks if th URI was correctly absolutized.
+         */
+        @GET
+        @Path("exceptionMapperAsync")
+        public void exceptionMapperAsync(@Suspended final AsyncResponse asyncResponse) {
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    asyncResponse.resume(new WebApplicationException());
+                }
+            }).start();
+        }
+
+        /**
+         * Resource method for testing for testing the URI absolutization in the exception mapper in the asynchronous case.
+         * A thread from executor thread pool (created out of request scope) is used for processing the resource method.
+         *
+         * Method always "throws" {@link WebApplicationException} (in case of async methods,
+         * the exceptions are not thrown directly, but passed to {@link AsyncResponse#resume(Throwable)}),
+         * which is defined to be handled by {@link TestExceptionMapper}.         *
+         * The exception mapper then creates the response with relative URI and response is routed
+         * into {@link UriCheckingResponseFilter}, which checks if th URI was correctly absolutized.
+         */
+        @GET
+        @Path("exceptionMapperExecutor")
+        public void exceptionMapperExecutor(@Suspended final AsyncResponse asyncResponse) {
+            executor.submit(new Runnable() {
+                @Override
+                public void run() {
+                    asyncResponse.resume(new WebApplicationException());
+                }
+            });
+        }
+
+        /**
+         * Resource method for testing correct baseUri and request overwrite in the prematching filter.
+         * Should never be called by the test, as {@link MyTest#redirectedUri()} should be called instead.
+         */
+        @GET
+        @Path("filterChangedBaseUri")
+        public Response locationWithChangedBaseUri() {
+            fail("Method should not expected to be called, as prematching filter should have changed the request uri.");
+            return Response.created(URI.create("new")).build();
+        }
+
+        /**
+         * Not called by the test directly, but after prematching filter redirect from
+         * {@link MyTest#locationWithChangedBaseUri()}.
+         *
+         * @return {@code 201 Created} response with location resolved against new baseUri.
+         */
+        @GET
+        @Path("newUri")
+        public Response redirectedUri() {
+            return Response.created(URI.create("newRedirected")).build();
+        }
+
+    }
+
+
+    /**
+     * Test the URI created in the post-matching request filter.
+     */
+    @Test
+    public void testAbortFilter() {
+        checkResponseFilter("ResponseTest/locationAborted", "uriAfterAbortion/SUCCESS");
+    }
+
+    /**
+     * Test the URI created in the pre-matching request filter.
+     */
+    @Test
+    public void testAbortPreMatchingFilter() {
+        checkResource("ResponseTest/locationAbortedPreMatching", "uriAfterPreMatchingAbortion/SUCCESS");
+    }
+
+
+    /**
+     * Test with URI Rewritten in the container response filter;
+     * Filters do have access to the response headers and can manipulate the location uri so that it contains a relative address.
+     * This test incorporates a filter which replaces the uri with a relative one. However we expect to have absolute uri at
+     * the end of the chain.
+     */
+    @Test
+    public void testAbsoluteUriWithFilter() {
+        checkResource("ResponseTest/locationTestWithFilter", "ResponseTest/UriChangedByFilter");
+    }
+
+    /**
+     * Test with URI Rewritten in the writer interceptor;
+     * Interceptors do have access to the response headers and can manipulate the location uri so that it contains a relative
+     * address.
+     * This test incorporates an interceptor which replaces the uri with a relative one. However we expect to have absolute uri
+     * at the end of the chain.
+     */
+    @Test
+    public void testAbsoluteUriWithInterceptor() {
+        checkResource("ResponseTest/locationWithInterceptor", "ResponseTest/UriChangedByInterceptor");
+    }
+
+
+    /**
+     * Test, that uri is correct in the response filter when created in the exception mapper (synchronous).
+     */
+    @Test
+    public void testExceptionMapperSync() {
+        checkResponseFilter("ResponseTest/exceptionMapperSync", "EXCEPTION_MAPPER");
+    }
+
+    /**
+     * Test, that uri is correct in the response filter when created in the exception mapper (asynchronous).
+     */
+    @Test
+    public void testExceptionMapperAsync() {
+        checkResponseFilter("ResponseTest/exceptionMapperAsync", "EXCEPTION_MAPPER");
+    }
+
+    /**
+     * Test, that uri is correct in the response filter when created in the exception mapper (asynchronous/executor).
+     */
+    @Test
+    public void testExceptionMapperExecutor() {
+        checkResponseFilter("ResponseTest/exceptionMapperExecutor", "EXCEPTION_MAPPER");
+    }
+
+    /**
+     * Test the baseUri and requestUri change in the prematching filter.
+     */
+    @Test
+    public void testLocationBaseUriChangedByPrematchingFilter() {
+        final Response response = target().path("ResponseTest/filterChangedBaseUri").request().get();
+        assertEquals("http://www.server.com/newRedirected", response.getHeaderString("Location"));
+    }
+
+    /**
+     * Test, that uri is correct in the response filter (synchronous).
+     */
+    @Test
+    public void testResponseFilterSync() {
+        checkResponseFilter("ResponseTest/responseFilterSync", "responseFilterSync");
+    }
+
+    /**
+     * Test, that uri is correct in the response filter (asynchronous).
+     */
+    @Test
+    public void testResponseFilterAsync() {
+        checkResponseFilter("ResponseTest/responseFilterAsync", "responseFilterAsync");
+    }
+
+
+
+    /**
+     * Test, that uri is correct in the response filter (asynchronous/executor).
+     */
+    @Test
+    public void testResponseFilterAsyncExecutor() {
+        checkResponseFilter("ResponseTest/responseFilterAsyncExecutor", "responseFilterAsyncExecutor");
+    }
+
+
+
+    /**
+     * Response filter - replaces the Location header with a relative uri.
+     */
+    public static class LocationManipulationFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
+                throws IOException {
+            final MultivaluedMap<String, ?> headers = responseContext.getHeaders();
+            final List<URI> locations = (List<URI>) headers.get(HttpHeaders.LOCATION);
+            locations.set(0, URI.create("ResponseTest/UriChangedByFilter"));
+            LOGGER.info("LocationManipulationFilter applied.");
+        }
+    }
+
+    /**
+     * Response filter - check if the URI is absolute. If it is not correctly absolutized,
+     * it changes the response to 500 - Internal Server Error and sets the error message into the response body.
+     */
+    public static class UriCheckingResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
+                throws IOException {
+            final URI location = responseContext.getLocation();
+            if (!location.isAbsolute()) {
+                responseContext.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+                responseContext.setEntity("Response location was not absolute in UriCheckingFilter. Location value: " + location);
+            }
+        }
+    }
+
+    /**
+     * Request Filter which aborts the current request calling ContainerRequestContext.abortWith().
+     *
+     * The returned response is passed to Response.created() and immediately tested for absolutization.
+     * This is necessary, as the relative URI would be absolutized later anyway and would reach the calling test method as
+     * an absolute URI and there would be no way to determine where the URI conversion was done.
+     *
+     * The result of the test is propagated back to the test method by separate URI values in case of a success or a failure.
+     */
+    public static class AbortingRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+            LOGGER.info("Aborting request in the request filter. Returning status created.");
+            final String successRelativeUri = "uriAfterAbortion/SUCCESS";
+            Response response = Response.created(URI.create(successRelativeUri)).build();
+
+            if (!response.getLocation().toString().equals(requestContext.getUriInfo().getBaseUri() + successRelativeUri)) {
+                response = Response.created(URI.create("uriAfterAbortion/FAILURE")).build();
+            }
+            requestContext.abortWith(response);
+        }
+    }
+
+    /**
+     * Request Filter which aborts the current request calling ContainerRequestContext.abortWith().
+     *
+     * The returned response is passed to Response.created() and immediately tested for absolutization.
+     * This is necessary, as the relative URI would be absolutized later anyway and would reach the calling test method as
+     * an absolute URI and there would be no way to determine where the URI conversion was done.
+     *
+     * The result of the test is propagated back to the test method by separate URI values in case of a success or a failure.
+     */
+    @PreMatching
+    public static class AbortingPreMatchingRequestFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+
+            if (requestContext.getUriInfo().getAbsolutePath().toString().endsWith("locationAbortedPreMatching")) {
+                LOGGER.info("Aborting request in the request filter. Returning status created.");
+                final String successRelativeUri = "uriAfterPreMatchingAbortion/SUCCESS";
+                Response response = Response.created(URI.create(successRelativeUri)).build();
+                if (!response.getLocation().toString().equals(requestContext.getUriInfo().getBaseUri() + successRelativeUri)) {
+                    response = Response.created(URI.create("uriAfterPreMatchingAbortion/FAILURE")).build();
+                }
+                requestContext.abortWith(response);
+            }
+        }
+    }
+
+    /**
+     * Request prematching filter which changes request URI and base URI.
+     *
+     * As a result, different resource mathod should be matched and invoked and return location resolved against the new
+     * base URI.
+     */
+    @PreMatching
+    public static class BaseUriChangingPreMatchingFilter implements ContainerRequestFilter {
+
+        @Override
+        public void filter(final ContainerRequestContext requestContext) throws IOException {
+            if (requestContext.getUriInfo().getAbsolutePath().toString().endsWith("filterChangedBaseUri")) {
+                final URI requestUri = requestContext.getUriInfo().getRequestUri();
+                // NOTE, that the trailing slash matters, without it, the URI is nod valid and is not correctly resolved by the
+                // URI.resolve() method.
+                final URI baseUri = URI.create("http://www.server.com/");
+                requestContext.setRequestUri(baseUri, requestUri.resolve("newUri"));
+            }
+        }
+    }
+
+    /**
+     * Writer interceptor - replaces the Location header with a relative uri.
+     */
+    public static class LocationManipulationInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final MultivaluedMap<String, ?> headers = context.getHeaders();
+            final List<URI> locations = (List<URI>) headers.get(HttpHeaders.LOCATION);
+            locations.set(0, URI.create("ResponseTest/UriChangedByInterceptor"));
+            LOGGER.info("LocationManipulationInterceptor applied.");
+            context.proceed();
+        }
+    }
+
+    /**
+     * Exception mapper which creates a test response with a relative URI.
+     */
+    public static class TestExceptionMapper implements ExceptionMapper<WebApplicationException> {
+
+        @Override
+        public Response toResponse(final WebApplicationException exception) {
+            exception.printStackTrace();
+            return Response.created(URI.create("EXCEPTION_MAPPER")).build();
+        }
+    }
+
+    /**
+     * Registers the filter and interceptor and binds it to the resource methods of interest.
+     */
+    public static class LocationManipulationDynamicBinding implements DynamicFeature {
+
+        @Override
+        public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
+            if (MyTest.class.equals(resourceInfo.getResourceClass())) {
+                final String methodName = resourceInfo.getResourceMethod().getName();
+                if (methodName.contains("locationTestWithFilter")) {
+                    context.register(LocationManipulationFilter.class);
+                    LOGGER.info("LocationManipulationFilter registered.");
+                }
+                if (methodName.contains("locationTestWithInterceptor")) {
+                    context.register(LocationManipulationInterceptor.class);
+                    LOGGER.info("LocationManipulationInterceptor registered.");
+                }
+                if (methodName.contains("locationTestAborted")) {
+                    context.register(AbortingRequestFilter.class);
+                    LOGGER.info("AbortingRequestFilter registered.");
+                }
+                if (methodName.contains("responseFilterSync")
+                        || methodName.contains("responseFilterAsync")
+                        || methodName.contains("locationTestAborted")
+                        || methodName.contains("exceptionMapperSync")
+                        || methodName.contains("exceptionMapperAsync")
+                        || methodName.contains("exceptionMapperExecutor")) {
+                    context.register(UriCheckingResponseFilter.class);
+                    LOGGER.info("UriCheckingResponseFilter registered.");
+                }
+                if (methodName.contains("locationWithChangedBaseUri")) {
+                    context.register(BaseUriChangingPreMatchingFilter.class);
+                    LOGGER.info("BaseUriChangingPreMatchingFilter registered.");
+                }
+            }
+        }
+    }
+
+    private Response checkResource(final String resourcePath, final String expectedRelativeUri) {
+        final Response response = target().path(resourcePath).request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals(getBaseUri() + expectedRelativeUri, location);
+        return response;
+    }
+
+    private void checkResponseFilter(final String resourcePath, final String expectedRelativeUri) {
+        final Response response = target().path(resourcePath).request().get(Response.class);
+        assertNotEquals("Message from response filter: " + response.readEntity(String.class),
+                response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+        assertEquals(getBaseUri() + expectedRelativeUri, response.getLocation().toString());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderWithAbsolutizationDisabledTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderWithAbsolutizationDisabledTest.java
new file mode 100644
index 0000000..5fd809b
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderWithAbsolutizationDisabledTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import java.net.URI;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Test if the location response header is left intact in case the
+ * {@link ServerProperties#LOCATION_HEADER_RELATIVE_URI_RESOLUTION_DISABLED} property is set to {@code true}.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class LocationHeaderWithAbsolutizationDisabledTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(LocationHeaderWithAbsolutizationDisabledTest.class.getName());
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        final ResourceConfig rc = new ResourceConfig(ResponseTest.class);
+        rc.property(ServerProperties.LOCATION_HEADER_RELATIVE_URI_RESOLUTION_DISABLED, Boolean.TRUE);
+        return rc;
+    }
+
+    /**
+     * Test JAX-RS resource
+     */
+    @Path(value = "test")
+    public static class ResponseTest {
+
+        /**
+         * Resource method for the basic uri test
+         * @return test response with relative location uri
+         */
+        @GET
+        @Path("location")
+        public Response locationTest() {
+            final URI uri = URI.create("location");
+            LOGGER.info("URI Created in the resource method > " + uri);
+            return Response.created(uri).build();
+        }
+
+        /**
+         * Resource method for the test with null location
+         * @return test response with null location uri
+         */
+        @GET
+        @Path("locationNull")
+        public Response locationTestNull() {
+            return Response.created(null).build();
+        }
+
+        /**
+         * Resource method for the test with location starting with single slash
+         * @return test response with relative location uri starting with slash
+         */
+        @GET
+        @Path("locationSlash")
+        public Response locationTestSlash() {
+            return Response.created(URI.create("/location")).build();
+        }
+    }
+
+    /**
+     * Test with relative location;
+     * Ensures, that the location remains intact
+     */
+    @Test
+    public void testLocation() {
+        final Response response = target().path("test/location").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals("location", location);
+    }
+
+    /**
+     * Test with relative location with leading slash
+     */
+    @Test
+    public void testLocationWithSlash() {
+        final Response response = target().path("test/locationSlash").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals("/location", location);
+    }
+
+    /**
+     * Test with relative location with leading slash
+     */
+    @Test
+    public void testNullLocation() {
+        final Response response = target().path("test/locationNull").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertNull(location);
+    }
+}
+
+
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderWithIncompatibleFlagTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderWithIncompatibleFlagTest.java
new file mode 100644
index 0000000..89b6e5c
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/LocationHeaderWithIncompatibleFlagTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, 2018 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.api;
+
+import java.net.URI;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Test if the location response header is resolved according to RFC7231 (in a JAX-RS 2.0 incompatible way) when
+ * {@link ServerProperties#LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231} property is set to {@code true} and
+ * {@link ServerProperties#LOCATION_HEADER_RELATIVE_URI_RESOLUTION_DISABLED} to {@code false}
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class LocationHeaderWithIncompatibleFlagTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(LocationHeaderWithIncompatibleFlagTest.class.getName());
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        final ResourceConfig rc = new ResourceConfig(ResponseTest.class);
+        rc.property(ServerProperties.LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231, Boolean.TRUE);
+        return rc;
+    }
+
+    /**
+     * Test JAX-RS resource
+     */
+    @Path(value = "test")
+    public static class ResponseTest {
+        /**
+         * Resource method for the basic uri test
+         * @return test response with relative location uri
+         */
+        @GET
+        @Path("location")
+        public Response locationTest() {
+            final URI uri = URI.create("location");
+            return Response.created(uri).build();
+        }
+
+        /**
+         * Resource method for the test with null location
+         * @return test response with null location uri
+         */
+        @GET
+        @Path("locationNull")
+        public Response locationTestNull() {
+            return Response.created(null).build();
+        }
+
+        /**
+         * Resource method for the test with location starting with single slash
+         * @return test response with relative location uri starting with slash
+         */
+        @GET
+        @Path("locationSlash")
+        public Response locationTestSlash() {
+            return Response.created(URI.create("/location")).build();
+        }
+    }
+
+    /**
+     * Test with relative location;
+     * Ensures, that the location remains intact
+     */
+    @Test
+    public void testLocation() {
+        final Response response = target().path("test/location").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals(getBaseUri().toString() + "test/location", location);
+    }
+
+    /**
+     * Test with relative location with leading slash
+     */
+    @Test
+    public void testLocationWithSlash() {
+        final Response response = target().path("test/locationSlash").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertEquals(getBaseUri() + "location", location);
+    }
+
+    /**
+     * Test with relative location with leading slash
+     */
+    @Test
+    public void testNullLocation() {
+        final Response response = target().path("test/locationNull").request(MediaType.TEXT_PLAIN).get(Response.class);
+        final String location = response.getHeaderString(HttpHeaders.LOCATION);
+        LOGGER.info("Location resolved from response > " + location);
+        assertNull(location);
+    }
+}
+
+
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MediaTypeProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MediaTypeProviderTest.java
new file mode 100644
index 0000000..99147b2
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MediaTypeProviderTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.util.HashMap;
+
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.MediaTypeProvider;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Media type provider tests.
+ *
+ * @author Mark Hadley
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class MediaTypeProviderTest {
+    @Test
+    public void testToString() {
+        final MediaType header = new MediaType("application", "xml");
+        final MediaTypeProvider instance = new MediaTypeProvider();
+
+        final String expResult = "application/xml";
+        final String result = instance.toString(header);
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testToStringWithParams() {
+        final HashMap<String, String> params = new HashMap<>();
+        params.put("charset", "utf8");
+        final MediaType header = new MediaType("application", "xml", params);
+        final MediaTypeProvider instance = new MediaTypeProvider();
+
+        final String expResult = "application/xml;charset=utf8";
+        final String result = instance.toString(header);
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testFromString() throws Exception {
+        final MediaTypeProvider instance = new MediaTypeProvider();
+
+        final String header = "application/xml";
+        final MediaType result = instance.fromString(header);
+        assertEquals(result.getType(), "application");
+        assertEquals(result.getSubtype(), "xml");
+        assertEquals(result.getParameters().size(), 0);
+    }
+
+    @Test
+    public void testFromStringWithParams() throws Exception {
+        final String header = "application/xml;charset=utf8";
+        final MediaTypeProvider instance = new MediaTypeProvider();
+
+        final MediaType result = instance.fromString(header);
+        assertEquals(result.getType(), "application");
+        assertEquals(result.getSubtype(), "xml");
+        assertEquals(result.getParameters().size(), 1);
+        assertTrue(result.getParameters().containsKey("charset"));
+        assertEquals(result.getParameters().get("charset"), "utf8");
+    }
+
+    @Test
+    public void testWithQuotedParam() {
+        final HashMap<String, String> params = new HashMap<String, String>();
+        params.put("foo", "\"bar\"");
+        final MediaType header = new MediaType("application", "xml", params);
+        final MediaTypeProvider instance = new MediaTypeProvider();
+
+        final String result = instance.toString(header);
+        final String expResult = "application/xml;foo=\"\\\"bar\\\"\"";
+        assertEquals(expResult, result);
+
+        final MediaType m = instance.fromString(result);
+        assertEquals("\"bar\"", m.getParameters().get("foo"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MessageBodyReaderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MessageBodyReaderTest.java
new file mode 100644
index 0000000..d0bd7f6
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MessageBodyReaderTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2013, 2018 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.api;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jan Supol (jan.supol at oracle.com)
+ * @author Michal Gajdos
+ */
+public class MessageBodyReaderTest extends JerseyTest {
+
+    @Path("resource")
+    public static class Resource {
+
+        @Context
+        private HttpHeaders headers;
+
+        @POST
+        @Path("plain")
+        public String plain(final EntityForReader entity) {
+            return entity.getValue() + ";" + headers.getHeaderString(HttpHeaders.CONTENT_TYPE);
+        }
+    }
+
+    @Provider
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public static class AppOctetReader implements MessageBodyReader<EntityForReader> {
+
+        @Override
+        public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                  final MediaType mediaType) {
+            return MediaType.APPLICATION_OCTET_STREAM_TYPE.equals(mediaType);
+        }
+
+        @Override
+        public EntityForReader readFrom(final Class<EntityForReader> type,
+                                        final Type genericType,
+                                        final Annotation[] annotations,
+                                        final MediaType mediaType,
+                                        final MultivaluedMap<String, String> httpHeaders,
+                                        final InputStream entityStream) throws IOException, WebApplicationException {
+            // Underlying stream should not be closed and Jersey is preventing from closing it.
+            entityStream.close();
+
+            return new EntityForReader(ReaderWriter.readFromAsString(entityStream, mediaType));
+        }
+    }
+
+    public static class EntityForReader {
+
+        private String value;
+
+        public EntityForReader(final String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class).register(LoggingFeature.class).register(AppOctetReader.class);
+    }
+
+    /**
+     * Test whether the default {@link MediaType} ({@value MediaType#APPLICATION_OCTET_STREAM}) is passed to a reader if no
+     * {@value HttpHeaders#CONTENT_TYPE} value is provided in a request.
+     */
+    @Test
+    public void testDefaultContentTypeForReader() throws Exception {
+        final HttpPost httpPost = new HttpPost(UriBuilder.fromUri(getBaseUri()).path("resource/plain").build());
+        httpPost.setEntity(new ByteArrayEntity("value".getBytes()));
+        httpPost.removeHeaders("Content-Type");
+
+        final HttpClient httpClient = HttpClientBuilder.create().build();
+        final HttpResponse response = httpClient.execute(httpPost);
+
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        assertEquals("value;null", ReaderWriter.readFromAsString(response.getEntity().getContent(), null));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MessageBodyWriterTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MessageBodyWriterTest.java
new file mode 100644
index 0000000..c9f051f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/MessageBodyWriterTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2012, 2018 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.api;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.ServerErrorException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Various MessageBodyWriter tests.
+ *
+ * @author Michal Gajdos
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class MessageBodyWriterTest extends JerseyTest {
+
+    private static final String HEADER_NAME = "MessageBodyWriterTestHeader";
+    private static final String HEADER_VALUE_CLIENT = "Client";
+    private static final String HEADER_VALUE_SERVER = "Server";
+
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, OverridingStringProvider.class, HtmlStringProvider.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(OverridingStringProvider.class);
+    }
+
+    @Provider
+    @Produces("text/plain")
+    public static class OverridingStringProvider implements MessageBodyWriter<String> {
+
+        @Context
+        private Configuration config;
+
+        @Override
+        public boolean isWriteable(
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(
+                final String t,
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(
+                final String t,
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType,
+                final MultivaluedMap<String, Object> httpHeaders,
+                final OutputStream entityStream) throws IOException, WebApplicationException {
+            // Underlying stream should not be closed and Jersey is preventing from closing it.
+            entityStream.close();
+
+            httpHeaders.putSingle(HEADER_NAME,
+                    config.getRuntimeType() == RuntimeType.SERVER ? HEADER_VALUE_SERVER : HEADER_VALUE_CLIENT);
+
+            entityStream.write(t.getBytes());
+        }
+    }
+
+    @Provider
+    @Produces("text/html")
+    public static class HtmlStringProvider implements MessageBodyWriter<String> {
+
+        @Context
+        private Configuration config;
+
+        @Override
+        public boolean isWriteable(
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType) {
+
+            if (MediaTypes.typeEqual(MediaType.TEXT_HTML_TYPE, mediaType)) {
+                final String charset = mediaType.getParameters().get("charset");
+                if (charset == null || !"utf-8".equalsIgnoreCase(charset)) {
+                    throw new ServerErrorException("Charset not received in isWritable()", 501);
+                }
+            }
+
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(
+                final String t,
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(
+                final String entity,
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType,
+                final MultivaluedMap<String, Object> httpHeaders,
+                final OutputStream entityStream) throws IOException, WebApplicationException {
+
+            final String charset = mediaType.getParameters().get("charset");
+            if (charset == null || !"utf-8".equalsIgnoreCase(charset)) {
+                throw new ServerErrorException("Charset not received in writeTo()", 502);
+            }
+
+            String html = "<html><body>" + entity + "</body></html>";
+            entityStream.write(html.getBytes());
+        }
+    }
+
+    /**
+     * Test resource.
+     */
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        public String post(@HeaderParam(HEADER_NAME) final String header, final String post) {
+            assertEquals(HEADER_VALUE_CLIENT, header);
+            return post;
+        }
+
+        @GET
+        @Produces("text/html;charset=utf-8")
+        @Path("html-charset-in-produces")
+        public String getHtmlForCharsetInProduces() {
+            return "foo";
+        }
+
+        @GET
+        @Path("html-charset-explicit-in-response")
+        public Response getHtmlForExplicitCharsetInResponse() {
+            return Response.ok("foo", MediaType.TEXT_HTML_TYPE.withCharset("utf-8")).build();
+        }
+
+        @GET
+        @Produces("text/html")
+        @Path("html-charset-in-accepts")
+        public String getHtmlForCharsetInAccepts() {
+            return "foo";
+        }
+
+    }
+
+    /**
+     * Test that it's possible to override default MessageBodyWriter instances.
+     */
+    @Test
+    public void testOverride() {
+        final Response response = target().request("text/plain").post(Entity.text("content"));
+
+        assertEquals("content", response.readEntity(String.class));
+        assertEquals(HEADER_VALUE_SERVER, response.getHeaderString(HEADER_NAME));
+    }
+
+    /**
+     * Test that media type parameters specified in @Produces or explicitly in a server response are received
+     * in the selected message body writer.
+     */
+    @Test
+    public void testMediaTypeParametersInMessageBodyWriter() {
+        Response response;
+
+        response = target().path("html-charset-in-produces").request("text/html").get();
+        assertThat("MediaType charset parameter in @Produces failed to propagate into MessageBodyWriter",
+                response.getStatus(), equalTo(200));
+        assertThat("Unexpected response content returned from server.",
+                response.readEntity(String.class), equalTo("<html><body>foo</body></html>"));
+
+        response = target().path("html-charset-explicit-in-response").request("text/html").get();
+        assertThat("MediaType charset parameter explicitly set in Response failed to propagate into MessageBodyWriter",
+                response.getStatus(), equalTo(200));
+        assertThat("Unexpected response content returned from server.",
+                response.readEntity(String.class), equalTo("<html><body>foo</body></html>"));
+
+        response = target().path("html-charset-in-accepts").request(MediaType.TEXT_HTML_TYPE.withCharset("utf-8")).get();
+        assertThat("MediaType charset parameter sent with request in Accept header failed to propagate into MessageBodyWriter",
+                response.getStatus(), equalTo(200));
+        assertThat("Unexpected response content returned from server.",
+                response.readEntity(String.class), equalTo("<html><body>foo</body></html>"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/OptionsTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/OptionsTest.java
new file mode 100644
index 0000000..dcc1a01
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/OptionsTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012, 2018 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.api;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class OptionsTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HttpOptionsTest.class);
+    }
+
+    @Path("/OptionsTest")
+    public static class HttpOptionsTest {
+
+        static String html_content =
+                "<html><head><title>get text/html</title></head>"
+                        + "<body>get text/html</body></html>";
+
+        @GET
+        public Response getPlain() {
+            return Response.ok("CTS-get text/plain").header("TEST-HEAD", "text-plain")
+                    .build();
+        }
+
+        @GET
+        @Produces("text/html")
+        public Response getHtml() {
+            return Response.ok(html_content).header("TEST-HEAD", "text-html")
+                    .build();
+        }
+
+        @GET
+        @Path("/sub")
+        public Response getSub() {
+            return Response.ok("TEST-get text/plain").header("TEST-HEAD",
+                    "sub-text-plain")
+                    .build();
+        }
+
+        @GET
+        @Path("/sub")
+        @Produces(value = "text/html")
+        public Response headSub() {
+            return Response.ok(html_content).header("TEST-HEAD", "sub-text-html")
+                    .build();
+        }
+    }
+
+    /*
+     * Client invokes OPTIONS on a sub resource at /OptionsTest/sub;
+     * which no request method designated for OPTIONS.
+     * Verify that an automatic response is generated.
+     */
+    @Test
+    public void OptionSubTest() {
+        final Response response = target().path("/OptionsTest/sub").request(MediaType.TEXT_HTML_TYPE).options();
+
+        assertTrue(response.getAllowedMethods().contains("GET"));
+        assertTrue(response.getAllowedMethods().contains("HEAD"));
+        assertTrue(response.getAllowedMethods().contains("OPTIONS"));
+
+        assertEquals(response.getMediaType(), MediaType.TEXT_HTML_TYPE);
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/QualitySourceMediaTypeProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/QualitySourceMediaTypeProviderTest.java
new file mode 100644
index 0000000..8371909
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/QualitySourceMediaTypeProviderTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.HttpHeaderReader;
+import org.glassfish.jersey.message.internal.QualitySourceMediaType;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author unknown
+ */
+public class QualitySourceMediaTypeProviderTest {
+
+    @Test
+    public void testOneMediaType() throws Exception {
+        final String header = "application/xml";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+        assertEquals(1, l.size());
+        final MediaType m = l.get(0);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testOneMediaTypeWithParameters() throws Exception {
+        final String header = "application/xml;charset=utf8";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(1, l.size());
+
+        final MediaType m = l.get(0);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        assertTrue(m.getParameters().containsKey("charset"));
+        assertEquals("utf8", m.getParameters().get("charset"));
+    }
+
+    @Test
+    public void testMultipleMediaType() throws Exception {
+        final String header = "application/xml, text/xml, text/html";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testMultipleMediaTypeWithQuality() throws Exception {
+        final String header = "application/xml;qs=0.1, text/xml;qs=0.2, text/html;qs=0.3";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testMultipleMediaTypeWithQuality2() throws Exception {
+        final String header = "application/xml;qs=0.1, text/xml;qs=0.2, text/html;qs=0.93";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testHttpURLConnectionAcceptHeader() throws Exception {
+        final String header = "text/html, image/gif, image/jpeg, */*; qs=.2";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(4, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("image", m.getType());
+        assertEquals("gif", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("image", m.getType());
+        assertEquals("jpeg", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testFirefoxAcceptHeader() throws Exception {
+        final String header = "text/xml,application/xml,application/xhtml+xml,text/html;qs=0.9,text/plain;qs=0.8,image/png,*/*;"
+                + "qs=0.5";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(7, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("application", m.getType());
+        assertEquals("xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("application", m.getType());
+        assertEquals("xhtml+xml", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("image", m.getType());
+        assertEquals("png", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(5);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(6);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifity() throws Exception {
+        final String header = "*/*, text/*, text/plain";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(3, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifityWithQuality() throws Exception {
+        final String header = "*/*, */*;qs=0.5, text/*, text/*;qs=0.5, text/plain, text/plain;qs=0.5";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        assertEquals(6, l.size());
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("text", m.getType());
+        assertEquals("plain", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(4);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(5);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifityHTTPExample1() throws Exception {
+        final String header = "text/*, text/html, text/html;level=1, */*";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+
+    @Test
+    public void testMediaTypeSpecifityHTTPExample2() throws Exception {
+        final String header = "text/*, text/html;level=1, text/html, */*";
+        final List<QualitySourceMediaType> l = HttpHeaderReader.readQualitySourceMediaType(header);
+
+        MediaType m;
+        m = l.get(0);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(1, m.getParameters().size());
+        m = l.get(1);
+        assertEquals("text", m.getType());
+        assertEquals("html", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(2);
+        assertEquals("text", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+        m = l.get(3);
+        assertEquals("*", m.getType());
+        assertEquals("*", m.getSubtype());
+        assertEquals(0, m.getParameters().size());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResourceContextTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResourceContextTest.java
new file mode 100644
index 0000000..2d2acdc
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResourceContextTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012, 2018 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.api;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test {@link ResourceContext}: resource context must provide access to
+ * sub-resources that can be provided by a custom component provider.
+ *
+ * @author Martin Grotzke
+ * @author Paul Sandoz
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ * @author Miroslav Fuksa
+ */
+public class ResourceContextTest extends JerseyTest {
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyRootResource.class);
+    }
+
+    @Path("/")
+    public static class MyRootResource {
+
+        @Context
+        ResourceContext resourceContext;
+
+        @Path("singleton")
+        public SingletonResource getSingletonResource() {
+            return resourceContext.getResource(SingletonResource.class);
+        }
+
+        @Path("perrequest")
+        public PerRequestResource getPerRequestSubResource() {
+            return resourceContext.getResource(PerRequestResource.class);
+        }
+
+        @Path("inject/{path}")
+        public InjectResource getInjectResource() {
+            final InjectResource resource = resourceContext.getResource(InjectResource.class);
+            resource.setPath("something");
+            return resourceContext.initResource(resource);
+        }
+
+        @Path("injectFromNewResource/{path}")
+        public InjectResource getInjectResourceFromNew() {
+            final InjectResource resource = new InjectResource();
+            resource.setPath("something");
+            return resourceContext.initResource(resource);
+        }
+
+    }
+
+    public static class InjectResource {
+        @PathParam("path")
+        private String path;
+
+        @GET
+        public String get() {
+            return path;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public void setPath(String path) {
+            this.path = path;
+        }
+    }
+
+
+    @Singleton
+    public static class SingletonResource {
+        int i;
+
+        @GET
+        public String get() {
+            i++;
+            return Integer.toString(i);
+        }
+    }
+
+    public static class PerRequestResource {
+        int i;
+
+        @GET
+        public String get() {
+            i++;
+            return Integer.toString(i);
+        }
+    }
+
+    @Test
+    public void testGetResourceFromResourceContext() {
+        assertEquals("1", target("/singleton").request().get(String.class));
+        assertEquals("2", target("/singleton").request().get(String.class));
+
+        assertEquals("1", target("/perrequest").request().get(String.class));
+        assertEquals("1", target("/perrequest").request().get(String.class));
+    }
+
+
+    @Test
+    public void testInitializeResourceFromResourceContext() {
+        assertEquals("aaa", target("/inject/aaa").request().get(String.class));
+        assertEquals("bbb", target("/inject/bbb").request().get(String.class));
+    }
+
+    @Test
+    public void testInitializeResourceFromNewResource() {
+        assertEquals("aaa", target("/injectFromNewResource/aaa").request().get(String.class));
+        assertEquals("bbb", target("/injectFromNewResource/bbb").request().get(String.class));
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseE2ETest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseE2ETest.java
new file mode 100644
index 0000000..b206551
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseE2ETest.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2013, 2018 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.api;
+
+import java.lang.annotation.Annotation;
+import java.net.URI;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.RuntimeDelegate;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.Uri;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Response E2E tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ResponseE2ETest extends JerseyTest {
+
+    /**
+     * Custom OK response.
+     */
+    public static class OkResponse extends Response {
+
+        private Response r;
+
+        /**
+         * Custom OK response constructor.
+         *
+         * @param entity entity content.
+         */
+        public OkResponse(String entity) {
+            r = Response.ok(entity).build();
+        }
+
+        @Override
+        public int getStatus() {
+            return r.getStatus();
+        }
+
+        @Override
+        public StatusType getStatusInfo() {
+            return r.getStatusInfo();
+        }
+
+        @Override
+        public Object getEntity() {
+            return r.getEntity();
+        }
+
+        @Override
+        public <T> T readEntity(Class<T> entityType) {
+            return r.readEntity(entityType);
+        }
+
+        @Override
+        public <T> T readEntity(GenericType<T> entityType) {
+            return r.readEntity(entityType);
+        }
+
+        @Override
+        public <T> T readEntity(Class<T> entityType, Annotation[] annotations) {
+            return r.readEntity(entityType, annotations);
+        }
+
+        @Override
+        public <T> T readEntity(GenericType<T> entityType, Annotation[] annotations) {
+            return r.readEntity(entityType, annotations);
+        }
+
+        @Override
+        public boolean hasEntity() {
+            return r.hasEntity();
+        }
+
+        @Override
+        public boolean bufferEntity() {
+            return r.bufferEntity();
+        }
+
+        @Override
+        public void close() {
+            r.close();
+        }
+
+        @Override
+        public MediaType getMediaType() {
+            return r.getMediaType();
+        }
+
+        @Override
+        public Locale getLanguage() {
+            return r.getLanguage();
+        }
+
+        @Override
+        public int getLength() {
+            return r.getLength();
+        }
+
+        @Override
+        public Set<String> getAllowedMethods() {
+            return r.getAllowedMethods();
+        }
+
+        @Override
+        public Map<String, NewCookie> getCookies() {
+            return r.getCookies();
+        }
+
+        @Override
+        public EntityTag getEntityTag() {
+            return r.getEntityTag();
+        }
+
+        @Override
+        public Date getDate() {
+            return r.getDate();
+        }
+
+        @Override
+        public Date getLastModified() {
+            return r.getLastModified();
+        }
+
+        @Override
+        public URI getLocation() {
+            return r.getLocation();
+        }
+
+        @Override
+        public Set<Link> getLinks() {
+            return r.getLinks();
+        }
+
+        @Override
+        public boolean hasLink(String relation) {
+            return r.hasLink(relation);
+        }
+
+        @Override
+        public Link getLink(String relation) {
+            return r.getLink(relation);
+        }
+
+        @Override
+        public Link.Builder getLinkBuilder(String relation) {
+            return r.getLinkBuilder(relation);
+        }
+
+        @Override
+        public MultivaluedMap<String, Object> getMetadata() {
+            return r.getMetadata();
+        }
+
+        @Override
+        public MultivaluedMap<String, Object> getHeaders() {
+            return r.getHeaders();
+        }
+
+        @Override
+        public MultivaluedMap<String, String> getStringHeaders() {
+            return r.getStringHeaders();
+        }
+
+        @Override
+        public String getHeaderString(String name) {
+            return r.getHeaderString(name);
+        }
+    }
+
+    @Path("response")
+    public static class ResponseTestResource {
+
+        @GET
+        @Path("custom")
+        public OkResponse subresponse() {
+            return new OkResponse("subresponse");
+        }
+
+        @GET
+        @Path("null")
+        public Response nullResponse() {
+            return null;
+        }
+
+        @GET
+        @Path("no-status-with-entity")
+        public Response entityResponseTest() {
+            return RuntimeDelegate.getInstance().createResponseBuilder().entity("1234567890").build();
+        }
+
+        @GET
+        @Path("no-status-without-entity")
+        public Response noEntityResponseTest() {
+            return RuntimeDelegate.getInstance().createResponseBuilder().build();
+        }
+
+        @Uri("response/internal")
+        WebTarget target;
+
+        @GET
+        @Path("external")
+        public Response external() {
+            Response response;
+            if (target == null) {
+                response = Response.serverError().entity("injected WebTarget is null").build();
+            } else {
+                response = target.request().buildGet().invoke();
+            }
+            return response;
+        }
+
+        @GET
+        @Path("internal")
+        public String internal() {
+            return "internal";
+        }
+
+        @PUT
+        @Path("not-modified-put")
+        public Response notModifiedPut(String data) {
+            return Response.notModified().entity("not-modified-" + data).build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ResponseTestResource.class);
+    }
+
+    /**
+     * JERSEY-1516 reproducer.
+     */
+    @Test
+    public void testCustomResponse() {
+        final Response response = target("response").path("custom").request().get();
+
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 200, response.getStatus());
+        assertEquals("Unexpected response entity.", "subresponse", response.readEntity(String.class));
+    }
+
+    /**
+     * JERSEY-1527 reproducer.
+     */
+    @Test
+    public void testNoStatusResponse() {
+        final WebTarget target = target("response").path("no-status-{param}-entity");
+        Response response;
+
+        response = target.resolveTemplate("param", "with").request().get();
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 200, response.getStatus());
+        assertEquals("Unexpected response entity.", "1234567890", response.readEntity(String.class));
+
+        response = target.resolveTemplate("param", "without").request().get();
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 204, response.getStatus());
+        assertFalse("Unexpected non-empty response entity.", response.hasEntity());
+    }
+
+    /**
+     * JERSEY-1528 reproducer.
+     */
+    @Test
+    public void testNullResponse() {
+        final Response response = target("response").path("null").request().get();
+
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 204, response.getStatus());
+        assertFalse("Unexpected non-empty response entity.", response.hasEntity());
+    }
+
+    /**
+     * JERSEY-1531 reproducer.
+     */
+    @Test
+    public void testInboundOutboundResponseMixing() {
+        final WebTarget target = target("response").path("external");
+        Response response;
+
+        response = target.request().get();
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", 200, response.getStatus());
+        assertEquals("Unexpected response entity.", "internal", response.readEntity(String.class));
+    }
+
+    /**
+     * JERSEY-845 reproducer.
+     *
+     * Verifies consistent behavior over time ("works as designed").
+     */
+    @Test
+    public void testEntityInNotModifiedPutResposne() {
+        final WebTarget target = target("response").path("not-modified-put");
+        Response response;
+
+        response = target.request().put(Entity.text("put-data"));
+        assertNotNull("Response is null.", response);
+        assertEquals("Unexpected response status.", Response.Status.NOT_MODIFIED.getStatusCode(), response.getStatus());
+        // response entity is dropped by server container in compliance with HTTP 1.1 spec
+        assertFalse("Unexpected response entity.", response.hasEntity());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseIntegrationTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseIntegrationTest.java
new file mode 100644
index 0000000..dc63ab4
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseIntegrationTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2012, 2018 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.api;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ResponseIntegrationTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(ResponseIntegrationTest.ResponseTest.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(ClientProperties.FOLLOW_REDIRECTS, false);
+    }
+
+    @Path(value = "/ResponseTest")
+    public static class ResponseTest {
+
+        @GET
+        @Produces(value = "text/plain")
+        public Response statusTest(@QueryParam("status") int status) {
+            Response res;
+            Response.ResponseBuilder resp;
+
+            switch (status) {
+                case 200:
+                    resp = Response.ok();
+                    break;
+                case 204:
+                case 201:
+                case 202:
+                case 303:
+                case 304:
+                case 307:
+                case 400:
+                case 401:
+                case 403:
+                case 404:
+                case 406:
+                case 409:
+                case 410:
+                case 415:
+                case 500:
+                case 503:
+                case 411:
+                case 412:
+                    resp = Response.status(status);
+                    break;
+                default:
+                    resp = Response.ok().entity("Unexpected parameter in request: " + status);
+                    break;
+            }
+
+            res = resp.header("TESTHEADER", "status code in request = " + status).build();
+            return res;
+        }
+    }
+
+    private void testStatus(int status) {
+        final Response response = target().path("ResponseTest").queryParam("status", status).request(MediaType.TEXT_PLAIN)
+                .get(Response.class);
+
+        assertEquals(status, response.getStatus());
+    }
+
+    private void testGenericStatus(int status) {
+        final GenericType<Response> genericType = new GenericType<>(Response.class);
+        final Response response = target().path("ResponseTest").queryParam("status", status).request(MediaType.TEXT_PLAIN)
+                .get(genericType);
+
+        assertEquals(status, response.getStatus());
+    }
+
+    /*
+     * Client send request to a resource,
+     * verify that correct status code returned
+     */
+    @Test
+    public void testStatuses() {
+        final int[] statuses = new int[] {
+                200,
+                201,
+                202,
+                204,
+                303,
+                304,
+                307,
+                401,
+                403,
+                404,
+                406,
+                409,
+                410,
+                411,
+                412,
+                415,
+                500,
+                503
+        };
+
+        for (Integer i : statuses) {
+            System.out.println("### Testing status: " + i);
+            testStatus(i);
+        }
+    }
+
+    /*
+     * Client send request to a resource,
+     * verify that correct status code returned
+     */
+    @Test
+    public void testGenericStatuses() {
+        final int[] statuses = new int[] {
+                200,
+                201,
+                202,
+                204,
+                303,
+                304,
+                307,
+                401,
+                403,
+                404,
+                406,
+                409,
+                410,
+                411,
+                412,
+                415,
+                500,
+                503
+        };
+
+        for (Integer i : statuses) {
+            System.out.println("### Testing status: " + i);
+            testGenericStatus(i);
+        }
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
new file mode 100644
index 0000000..be018cc
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ResponseTest.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2012, 2018 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.api;
+
+import java.security.AccessController;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.message.internal.HeaderUtils;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ResponseTest {
+
+    /*
+     * Create an instance of Response using Response.ok(String, Variant).build()
+     * verify that correct status code is returned
+     */
+    @Test
+    public void OkTest5() {
+        Response resp;
+        int status = 200;
+        String content = "Test Only";
+        List<String> encoding = Arrays.asList("gzip", "compress");
+        List<String> lang = Arrays.asList("en-US", "en-GB", "zh-CN");
+
+        MediaType mt = new MediaType("text", "plain");
+        List<Variant> vts = Variant.VariantListBuilder.newInstance().mediaTypes(mt)
+                .languages(new Locale("en", "US"), new Locale("en", "GB"),
+                        new Locale("zh", "CN")).encodings((String[]) encoding.toArray())
+                .add().build();
+
+        String tmp;
+        for (Variant vt : vts) {
+            resp = Response.ok(content, vt).build();
+            tmp = verifyResponse(resp, content, status, encoding, lang, null,
+                    null, null, null);
+            if (tmp.endsWith("false")) {
+                System.out.println("### " + tmp);
+                fail();
+            }
+        }
+    }
+
+    /*
+     * Create an instance of Response using
+     * Response.ResponseBuilder.clone()
+     * verify that correct status code is returned
+     */
+    @Test
+    public void cloneTest() throws CloneNotSupportedException {
+        StringBuilder sb = new StringBuilder();
+
+        int status = 200;
+        List<String> type = Arrays.asList("text/plain", "text/html");
+        List<String> encoding = Arrays.asList("gzip", "compress");
+        List<String> lang = Arrays.asList("en-US", "en-GB", "zh-CN");
+
+        String name = "name_1";
+        String value = "value_1";
+        Cookie ck1 = new Cookie(name, value);
+        NewCookie nck1 = new NewCookie(ck1);
+
+        List<String> cookies = Arrays.asList(nck1.toString().toLowerCase());
+
+        Response.ResponseBuilder respb1 = Response.status(status)
+                .header("Content-type", "text/plain").header("Content-type",
+                "text/html").header("Content-Language", "en-US")
+                .header("Content-Language", "en-GB").header("Content-Language",
+                "zh-CN").header("Cache-Control", "no-transform")
+                .header("Set-Cookie", "name_1=value_1;version=1");
+        Response.ResponseBuilder respb2 = respb1.clone();
+
+        Response resp2 = respb2.build();
+
+        String tmp = verifyResponse(resp2, null, status, encoding, lang, type,
+                null, null, cookies);
+        if (tmp.endsWith("false")) {
+            System.out.println("### " + sb.toString());
+            fail();
+        }
+        sb.append(tmp).append(newline);
+
+        String content = "TestOnly";
+        Response resp1 = respb1.entity(content).cookie((NewCookie) null).build();
+        tmp = verifyResponse(resp1, content, status, encoding, lang, type,
+                null, null, null);
+        if (tmp.endsWith("false")) {
+            System.out.println("### " + sb.toString());
+            fail();
+        }
+
+        MultivaluedMap<java.lang.String, java.lang.Object> mvp =
+                resp1.getMetadata();
+        if (mvp.containsKey("Set-Cookie")) {
+            sb.append("Response contains unexpected Set-Cookie: ").append(mvp.getFirst("Set-Cookie").toString()).append(newline);
+            System.out.println("### " + sb.toString());
+            fail();
+        }
+        sb.append(tmp).append(newline);
+    }
+
+    /*
+     * Create an instance of Response using
+     * Response.fromResponse(Response).build()
+     * verify that correct status code is returned
+     */
+    @Test
+    public void fromResponseTest() {
+        int status = 200;
+        String content = "Test Only";
+        List<String> type = Arrays.asList("text/plain", "text/html");
+        List<String> encoding = Arrays.asList("gzip", "compress");
+        List<String> lang = Arrays.asList("en-US", "en-GB", "zh-CN");
+
+        MediaType mt1 = new MediaType("text", "plain");
+        MediaType mt2 = new MediaType("text", "html");
+        List<Variant> vts = Variant.VariantListBuilder.newInstance().mediaTypes(mt1, mt2)
+                .languages(new Locale("en", "US"), new Locale("en", "GB"),
+                        new Locale("zh", "CN")).encodings((String[]) encoding.toArray())
+                .add().build();
+
+        String tmp;
+        for (Variant vt : vts) {
+            Response resp1 = Response.ok(content, vt).build();
+            Response resp = Response.fromResponse(resp1).build();
+            tmp = verifyResponse(resp, content, status, encoding, lang, type,
+                    null, null, null);
+            if (tmp.endsWith("false")) {
+                System.out.println("### " + tmp);
+                fail();
+            }
+        }
+    }
+
+    /*
+     * Create an instance of Response using
+     * Response.ResponseBuilder.header(String, Object).build()
+     * verify that correct status code is returned
+     */
+    @Test
+    public void headerTest() {
+        int status = 200;
+        List<String> type = Arrays.asList("text/plain", "text/html");
+        List<String> encoding = Arrays.asList("gzip", "compress");
+        List<String> lang = Arrays.asList("en-US", "en-GB", "zh-CN");
+
+        String name = "name_1";
+        String value = "value_1";
+        Cookie ck1 = new Cookie(name, value);
+        NewCookie nck1 = new NewCookie(ck1);
+
+        List<String> cookies = Arrays.asList(nck1.toString().toLowerCase());
+
+        Response resp = Response.status(status).header("Content-type",
+                "text/plain").header("Content-type", "text/html").header("Content-Language", "en-US")
+                .header("Content-Language", "en-GB").header("Content-Language",
+                "zh-CN").header("Cache-Control", "no-transform")
+                .header("Set-Cookie", "name_1=value_1;version=1").build();
+        String tmp = verifyResponse(resp, null, status, encoding, lang, type,
+                null, null, cookies);
+        if (tmp.endsWith("false")) {
+            System.out.println("### " + tmp);
+            fail();
+        }
+    }
+
+    /*
+     * Create an instance of Response using
+     * Response.status(int).variant(Variant).build()
+     * verify that correct status code is returned
+     */
+    @Test
+    public void variantTest() {
+        Response resp;
+        int status = 200;
+        List<String> encoding = Arrays.asList("gzip", "compress");
+        List<String> lang = Arrays.asList("en-US", "en-GB", "zh-CN");
+
+        MediaType mt = new MediaType("text", "plain");
+        List<Variant> vts = Variant.VariantListBuilder.newInstance().mediaTypes(mt)
+                .languages(new Locale("en", "US"), new Locale("en", "GB"),
+                        new Locale("zh", "CN")).encodings((String[]) encoding.toArray())
+                .add().build();
+
+        String tmp;
+        for (Variant vt : vts) {
+            resp = Response.status(status).variant(vt).build();
+            tmp = verifyResponse(resp, null, status, encoding, lang, null, null,
+                    null, null);
+            if (tmp.endsWith("false")) {
+                System.out.println("### " + tmp);
+                fail();
+            }
+        }
+    }
+
+    private static final String indent = "    ";
+    private static final String newline = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("line.separator"));
+
+    private String verifyResponse(Response resp, String content, int status,
+                                  List<String> encoding, List<String> language, List<String> type,
+                                  List<String> var, List<String> ccl, List<String> cookies) {
+        boolean pass = true;
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("========== Verifying a Response: ").append(newline);
+
+        String tmp = verifyResponse(resp, content, status, null);
+        sb.append(indent).append(tmp).append(newline);
+        if (tmp.endsWith("false")) {
+            pass = false;
+        }
+
+        MultivaluedMap<String, String> mvp = HeaderUtils.asStringHeaders(
+                resp.getMetadata());
+
+        for (String key : mvp.keySet()) {
+            sb.append(indent + "Processing Key found in response: ").append(key).append(": ").append(mvp.get(key)).append("; ")
+                    .append(newline);
+
+            if (key.equalsIgnoreCase("Vary")) {
+                for (String value : var) {
+                    String actual = mvp.get(key).toString().toLowerCase();
+                    if (!actual.contains(value)) {
+                        pass = false;
+                        sb.append(indent + indent + "Expected header ").append(value).append(" not set in Vary.").append(newline);
+                    } else {
+                        sb.append(indent + indent + "Found expected header ").append(value).append(".").append(newline);
+                    }
+                }
+            }
+
+            if (encoding != null) {
+                if (key.equalsIgnoreCase("Content-encoding")) {
+                    for (Object enc : mvp.get(key)) {
+                        if (!encoding.contains(enc.toString().toLowerCase())) {
+                            pass = false;
+                            sb.append(indent + indent + "Encoding test failed: ").append(newline);
+                        }
+                    }
+                }
+            }
+
+            if (language != null) {
+                if (key.equalsIgnoreCase("Content-language")) {
+                    for (String lang : mvp.get(key)) {
+                        if (!language.contains(lang)) {
+                            pass = false;
+                            sb.append(indent + indent + "language test failed: ").append(lang)
+                                    .append(" is not expected in Response").append(newline);
+                            for (String tt : language) {
+                                sb.append(indent + indent + "Expecting Content-Language ").append(tt).append(newline);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (type != null) {
+                if (key.equalsIgnoreCase("Content-Type")) {
+                    for (Object lang : mvp.get(key)) {
+                        if (!type.contains(lang.toString().toLowerCase())) {
+                            pass = false;
+                            sb.append(indent + indent + "Content-Type test failed: ").append(lang)
+                                    .append(" is not expected in Response").append(newline);
+                        }
+                    }
+                }
+            }
+
+            if (ccl != null) {
+                for (String tt : ccl) {
+                    sb.append("Expecting Cache-Control ").append(tt).append(newline);
+                }
+                if (key.equalsIgnoreCase("Cache-Control")) {
+                    for (Object all_ccl : mvp.get(key)) {
+                        for (String cc : ccl) {
+                            if (!(all_ccl.toString().toLowerCase().contains(cc.toLowerCase()))) {
+                                pass = false;
+                                sb.append(indent + indent + "Cache-Control test failed: ").append(cc)
+                                        .append(" is not found in Response.").append(newline);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (cookies != null) {
+                for (String tt : cookies) {
+                    sb.append(indent + indent + "Expecting Set-Cookie").append(tt).append(newline);
+                }
+                if (key.equalsIgnoreCase("Set-Cookie")) {
+                    for (Object nck_actual : mvp.get(key)) {
+                        sb.append(indent + indent + "Processing ").append(nck_actual.toString()).append(newline);
+                        if (!cookies.contains(nck_actual.toString().toLowerCase()
+                                .replace(" ", ""))) {
+                            pass = false;
+                            sb.append(indent + indent + "Set-Cookie test failed: ").append(nck_actual)
+                                    .append(" is not expected in Response.").append(newline);
+                        } else {
+                            sb.append(indent + indent + "Expected Set-Cookie: ").append(nck_actual)
+                                    .append(" is found in Response.").append(newline);
+                        }
+                    }
+                }
+            }
+        }
+
+        sb.append(indent).append(pass);
+
+        return sb.toString();
+    }
+
+    private String verifyResponse(Response resp, String content, int status, HashMap<String, String> expected_map) {
+        boolean pass = true;
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("========== Verifying a Response with Map: ").append(newline);
+
+        if ((content == null) || (content.equals(""))) {
+            if (!(resp.getEntity() == null) || "".equals(resp.getEntity())) {
+                pass = false;
+                sb.append(indent + "Entity verification failed: expecting no content, got ").append((String) resp.getEntity())
+                        .append(newline);
+            }
+        } else if (!content.equals(resp.getEntity())) {
+            pass = false;
+            sb.append(indent + "Entity verification failed: expecting ").append(content).append(", got ")
+                    .append((String) resp.getEntity()).append(newline);
+        } else {
+            sb.append(indent + "Correct content found in Response: ").append((String) resp.getEntity()).append(newline);
+        }
+
+        if (resp.getStatus() != status) {
+            pass = false;
+            sb.append(indent + "Status code verification failed: expecting ").append(status).append(", got ")
+                    .append(resp.getStatus()).append(newline);
+        } else {
+            sb.append(indent + "Correct status found in Response: ").append(status).append(newline);
+        }
+
+        MultivaluedMap<java.lang.String, java.lang.Object> mvp =
+                resp.getMetadata();
+        if (expected_map == null) {
+            sb.append(indent + "No keys to verify or expected, but found the following keys in Response:").append(newline);
+            for (String key : mvp.keySet()) {
+                sb.append(indent + indent + "Key: ").append(key).append("; ").append(mvp.getFirst(key)).append(";")
+                        .append(newline);
+            }
+        } else {
+            for (String key_actual : mvp.keySet()) {
+                sb.append(indent + "Response contains key: ").append(key_actual).append(newline);
+            }
+            sb.append(indent + "Verifying the following keys in Response:").append(newline);
+            String actual, expected;
+            for (String key : expected_map.keySet()) {
+                if (!mvp.containsKey(key)) {
+                    pass = false;
+                    sb.append(indent + indent + "Key: ").append(key).append(" is not found in Response;").append(newline);
+                } else if (key.equalsIgnoreCase("last-modified")) {
+                    sb.append(indent + indent + "Key Last-Modified is found in response").append(newline);
+                } else {
+                    expected = expected_map.get(key).toLowerCase();
+                    actual = mvp.getFirst(key).toString().toLowerCase();
+
+                    if (actual.startsWith("\"") && actual.endsWith("\"")) {
+                        actual = actual.substring(1, actual.length() - 1);
+                    }
+
+                    if (!actual.equals(expected)) {
+                        pass = false;
+                        sb.append(indent + indent + "Key: ").append(key).append(" found in Response, but with different value;")
+                                .append(newline);
+                        sb.append(indent + indent + "Expecting ").append(expected_map.get(key)).append("; got ")
+                                .append(mvp.getFirst(key)).append(newline);
+                    }
+                    sb.append(indent + indent + "Processed key ").append(key).append(" with expected value ")
+                            .append(expected_map.get(key)).append(newline);
+                }
+            }
+        }
+        sb.append(indent).append(pass);
+        return sb.toString();
+    }
+
+    @Test
+    public void testAllowString() {
+        Response.ResponseBuilder responseBuilder = Response.ok();
+
+        responseBuilder = responseBuilder.allow("GET");
+        assertTrue(responseBuilder.build().getHeaderString(HttpHeaders.ALLOW).contains("GET"));
+        responseBuilder = responseBuilder.allow((String) null);
+        assertTrue(responseBuilder.build().getHeaderString(HttpHeaders.ALLOW) == null);
+    }
+
+    @Test
+    public void testAllowSet() {
+        Response.ResponseBuilder responseBuilder = Response.ok();
+
+        responseBuilder = responseBuilder.allow(new HashSet<>(Arrays.asList("GET")));
+        assertTrue(responseBuilder.build().getHeaderString(HttpHeaders.ALLOW).contains("GET"));
+        responseBuilder = responseBuilder.allow((Set<String>) null);
+        assertEquals(null, responseBuilder.build().getHeaderString(HttpHeaders.ALLOW));
+    }
+
+    @Test
+    public void testAllowVariant() {
+        Response.ResponseBuilder responseBuilder = Response.ok();
+
+        responseBuilder = responseBuilder.allow(new HashSet<>(Arrays.asList("GET")));
+        assertTrue(responseBuilder.build().getHeaderString(HttpHeaders.ALLOW).contains("GET"));
+        responseBuilder = responseBuilder.allow((String[]) null);
+        assertEquals(null, responseBuilder.build().getHeaderString(HttpHeaders.ALLOW));
+    }
+
+    @Test
+    public void bufferEntityTest() {
+        Response response = Response.ok().build();
+        response.close();
+        try {
+            response.bufferEntity();
+            fail("IllegalStateException expected when reading entity after response has been closed.");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void getEntityTest() {
+        Response response = Response.ok().build();
+        response.close();
+        try {
+            response.getEntity();
+            fail("IllegalStateException expected when reading entity after response has been closed.");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void hasEntityTest() {
+        Response response = Response.ok().build();
+        response.close();
+        try {
+            response.hasEntity();
+            fail("IllegalStateException expected when reading entity after response has been closed.");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    // Reproducer for JERSEY-1553
+    @Test
+    public void testVariants() {
+        List<String> encoding = Arrays.asList("gzip", "compress");
+        List<Variant> list = Variant.VariantListBuilder
+                .newInstance()
+                .mediaTypes(MediaType.TEXT_PLAIN_TYPE)
+                .languages(new Locale("en", "US"), new Locale("en", "GB"))
+                .encodings(encoding.toArray(new String[encoding.size()])).add().build();
+
+        final Response r1 = Response.ok().variants(list).build();
+        assertNotNull(r1);
+        assertNotNull(r1.getHeaderString(HttpHeaders.VARY));
+
+        final Response r2 = Response.ok().variants(list.toArray(new Variant[list.size()])).build();
+        assertNotNull(r2);
+        assertNotNull(r2.getHeaderString(HttpHeaders.VARY));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/RestrictedHeaderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/RestrictedHeaderTest.java
new file mode 100644
index 0000000..06568bd
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/RestrictedHeaderTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2013, 2018 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.api;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Test setting headers that are restricted by {@link java.net.HttpURLConnection}.
+ *
+ * @author Miroslav Fuksa
+ */
+public class RestrictedHeaderTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(RestrictedHeaderTest.class.getName());
+
+    @Path("/")
+    public static class MyResource {
+
+        @GET
+        public Response getOptions(@Context HttpHeaders headers) {
+            MultivaluedMap<String, String> requestHeaders = headers.getRequestHeaders();
+            System.out.println("Headers: " + requestHeaders);
+            if (requestHeaders.containsKey("Origin") || requestHeaders.containsKey("Access-Control-Request-Method")) {
+                LOGGER.info("CORS headers found.");
+                return Response.ok().build();
+            }
+            LOGGER.info("CORS headers are missing. ");
+            return Response.serverError().entity("CORS headers are missing").build();
+        }
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new ResourceConfig(MyResource.class, LoggingFeature.class);
+    }
+
+    @Ignore("The setting of allowRestrictedHeaders system property is global and cached. Only "
+            + "one of both testForbiddenHeadersNotAllowed() and testForbiddenHeadersAllowed() can be run during one test.")
+    @Test
+    public void testForbiddenHeadersNotAllowed() {
+        Client client = ClientBuilder.newClient();
+        Response response = testHeaders(client);
+        Assert.assertEquals(500, response.getStatus());
+    }
+
+    /**
+     * Tests sending of restricted headers (Origin and Access-Control-Request-Method) which are
+     * used for CORS. These headers are by default skipped by the {@link java.net.HttpURLConnection}.
+     * The system property {@code sun.net.http.allowRestrictedHeaders} must be defined in order to
+     * allow these headers.
+     */
+    @Test
+    public void testForbiddenHeadersAllowed() {
+        Client client = ClientBuilder.newClient();
+        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
+
+        Response response = testHeaders(client);
+        System.out.println(response.readEntity(String.class));
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    /**
+     * Same as {@link #testForbiddenHeadersAllowed()} ()} but uses {@link org.glassfish.jersey.apache.connector
+     * .ApacheConnector} connector
+     * which allows modification of these headers.
+     */
+    @Test
+    public void testForbiddenHeadersWithApacheConnector() {
+        ClientConfig clientConfig = new ClientConfig();
+        clientConfig.connectorProvider(new ApacheConnectorProvider());
+        Client client = ClientBuilder.newClient(clientConfig);
+        testHeaders(client);
+    }
+
+    private Response testHeaders(Client client) {
+        client.register(LoggingFeature.class);
+        Invocation.Builder builder = client.target(getBaseUri()).path("/").request()
+                .header("Origin", "http://example.com")
+                .header("Access-Control-Request-Method", "POST")
+                .header("Testus", "Hello");
+        return builder.get();
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/SourceEntityProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/SourceEntityProviderTest.java
new file mode 100644
index 0000000..47b6173
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/SourceEntityProviderTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2012, 2018 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.api;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test of {@link javax.xml.transform.Source Source} MessageBody Provider
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class SourceEntityProviderTest extends JerseyTest {
+
+    private static final String prefix = "<?xml version=\"1.0\" encoding=\"UTF-8\"";
+    private static final String xdkPrefix = "<?xml version = '1.0' encoding = 'UTF-8'?>";
+    private static final String entity = prefix + "?><test><aaa/></test>";
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    private static String extractContent(Source source) throws TransformerFactoryConfigurationError,
+            TransformerConfigurationException, TransformerException {
+        TransformerFactory transFactory = TransformerFactory.newInstance();
+
+        // identity transformation
+        Transformer transformer = transFactory.newTransformer();
+
+        StringWriter writer = new StringWriter();
+        Result result = new StreamResult(writer);
+        transformer.transform(source, result);
+        return writer.toString();
+    }
+
+    @Test
+    public void sourceProviderTest() throws IOException, TransformerConfigurationException, TransformerFactoryConfigurationError,
+            TransformerException {
+        Source source = new StreamSource(new ByteArrayInputStream(entity.getBytes()));
+
+        Response response = target().path("test").path("source").request().put(Entity.entity(source, MediaType.TEXT_XML_TYPE));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertTrue(response.readEntity(String.class).startsWith(StreamSource.class.toString()));
+    }
+
+    @Test
+    public void streamProviderTest() throws IOException {
+        StreamSource source = new StreamSource(new ByteArrayInputStream(entity.getBytes()));
+
+        Response response = target().path("test").path("stream").request().put(Entity.entity(source, MediaType.TEXT_XML_TYPE));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertTrue(response.readEntity(String.class).startsWith(StreamSource.class.toString()));
+    }
+
+    @Test
+    public void saxProviderTest() throws IOException, SAXException, ParserConfigurationException {
+        SAXSource source = createSAXSource(entity);
+
+        Response response = target().path("test").path("sax").request().put(Entity.entity(source, MediaType.TEXT_XML_TYPE));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertTrue(response.readEntity(String.class).startsWith(SAXSource.class.toString()));
+    }
+
+    @Test
+    public void domProviderTest() throws IOException, SAXException, ParserConfigurationException {
+        DOMSource source = createDOMSoruce(entity);
+
+        Response response = target().path("test").path("dom").request().put(Entity.entity(source, MediaType.TEXT_XML_TYPE));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        assertTrue(response.readEntity(String.class).startsWith(DOMSource.class.toString()));
+    }
+
+    @Test
+    public void getSourceTest() throws Exception {
+        Response response = target().path("test").path("source").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String content = extractContent(response.readEntity(Source.class));
+        assertTrue(content.startsWith(prefix) || content.startsWith(xdkPrefix));
+    }
+
+    @Test
+    public void getStreamSourceTest() throws Exception {
+        Response response = target().path("test").path("stream").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String content = extractContent(response.readEntity(StreamSource.class));
+        assertTrue(content.startsWith(prefix) || content.startsWith(xdkPrefix));
+    }
+
+    @Test
+    public void getSaxSourceTest() throws Exception {
+        Response response = target().path("test").path("sax").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String content = extractContent(response.readEntity(SAXSource.class));
+        assertTrue("Content '" + content + "' does not start with the expected prefix '" + prefix + "'",
+                content.startsWith(prefix) || content.startsWith(xdkPrefix));
+    }
+
+    @Test
+    public void getDomSourceTest() throws Exception {
+        Response response = target().path("test").path("dom").request().get();
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String content = extractContent(response.readEntity(DOMSource.class));
+        assertTrue("Content '" + content + "' does not start with the expected prefix '" + prefix + "'",
+                content.startsWith(prefix) || content.startsWith(xdkPrefix));
+    }
+
+    private static SAXSource createSAXSource(String content) throws SAXException, ParserConfigurationException {
+        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
+        return new SAXSource(saxFactory.newSAXParser().getXMLReader(), new InputSource(new ByteArrayInputStream(
+                content.getBytes())));
+    }
+
+    private static DOMSource createDOMSoruce(String content) throws SAXException, IOException, ParserConfigurationException {
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        Document d = documentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(content.getBytes()));
+        return new DOMSource(d);
+    }
+
+    @Path("test")
+    public static class TestResource {
+
+        @PUT
+        @Consumes("text/xml")
+        @Path("source")
+        public String putSourceAndReturnString(Source source) throws IOException, TransformerException {
+            return source.getClass() + extractContent(source);
+        }
+
+        @PUT
+        @Consumes("text/xml")
+        @Path("stream")
+        public String putStreamSourceAndReturnString(StreamSource source) throws IOException, TransformerException {
+            return source.getClass() + extractContent(source);
+        }
+
+        @PUT
+        @Consumes("text/xml")
+        @Path("sax")
+        public String putSaxSourceAndReturnString(SAXSource source) throws IOException, TransformerException {
+            return source.getClass() + extractContent(source);
+        }
+
+        @PUT
+        @Consumes("text/xml")
+        @Path("dom")
+        public String putDomSourceAndReturnString(DOMSource source) throws IOException, TransformerException {
+            return source.getClass() + extractContent(source);
+        }
+
+        @GET
+        @Produces("application/xml")
+        @Path("source")
+        public StreamSource getSource() {
+            return new StreamSource(new ByteArrayInputStream(entity.getBytes()));
+        }
+
+        @GET
+        @Produces("application/xml")
+        @Path("stream")
+        public StreamSource getStreamSource() {
+            return new StreamSource(new ByteArrayInputStream(entity.getBytes()));
+        }
+
+        @GET
+        @Produces("application/xml")
+        @Path("sax")
+        public SAXSource getSaxSource() throws SAXException, ParserConfigurationException {
+            return createSAXSource(entity);
+        }
+
+        @GET
+        @Produces("application/xml")
+        @Path("dom")
+        public DOMSource getDomSource() throws Exception {
+            return createDOMSoruce(entity);
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/UnsafeCharsInUriTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/UnsafeCharsInUriTest.java
new file mode 100644
index 0000000..79edd3f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/UnsafeCharsInUriTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, 2018 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.api;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * Test if URI can contain unsafe characters in the query parameter, e.g. for sending JSON
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class UnsafeCharsInUriTest extends JerseyTest {
+    @Override
+    protected ResourceConfig configure() {
+        ResourceConfig rc = new ResourceConfig(UnsafeCharsInUriTest.ResponseTest.class);
+        return rc;
+    }
+
+    /**
+     * Test resource
+     */
+    @Path(value = "/app")
+    public static class ResponseTest {
+        /**
+         * Test resource method returning the content of the {@code msg} query parameter.
+         *
+         * @return the {@code msg} query parameter (as received)
+         */
+        @GET
+        @Path("test")
+        public Response jsonQueryParamTest(@DefaultValue("") @QueryParam("msg") final String msg) {
+            return Response.ok().entity(msg).build();
+        }
+
+    }
+
+    /**
+     * Test, that server can consume JSON (curly brackets) and other unsafe characters sent in the query parameter
+     *
+     * @throws IOException
+     */
+    @Test
+    public void testSpecCharsInUriWithSockets() throws IOException {
+        // quotes are encoded by browsers, curly brackets are not, so the quotes will be sent pre-encoded
+        // HTTP 1.0 is used for simplicity
+        String response = sendGetRequestOverSocket(getBaseUri(), "GET /app/test?msg={%22foo%22:%22bar%22} HTTP/1.0");
+        assertArrayEquals("{\"foo\":\"bar\"}".getBytes(Charset.forName("ISO-8859-1")), response.getBytes());
+    }
+
+    @Test
+    @Ignore("Incorrectly written test (doesn't deal with http encoding).")
+    public void testSecialCharsInQueryParam() throws IOException {
+        // quotes are encoded by browsers, curly brackets are not, so the quotes will be sent pre-encoded
+        // HTTP 1.0 is used for simplicity
+        String response = sendGetRequestOverSocket(getBaseUri(),
+                                            "GET /app/test?msg=Hello\\World+With+SpecChars+§*)$!±@-_=;`:\\,~| HTTP/1.0");
+
+        assertArrayEquals("Hello\\World With SpecChars §*)$!±@-_=;`:\\,~|".getBytes(Charset.forName("ISO-8859-1")),
+                     response.getBytes());
+    }
+
+    private String sendGetRequestOverSocket(final URI baseUri, final String requestLine) throws IOException {
+        // Low level approach with sockets is used, because common Java HTTP clients are using java.net.URI,
+        // which fails when unencoded curly bracket is part of the URI
+        final Socket socket = new Socket(baseUri.getHost(), baseUri.getPort());
+        final PrintWriter pw =
+                new PrintWriter(
+                        new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), Charset.forName("ISO-8859-1"))));
+
+        pw.println(requestLine);
+        pw.println(); // http request should end with a blank line
+        pw.flush();
+
+        final BufferedReader br =
+                new BufferedReader(new InputStreamReader(socket.getInputStream(), Charset.forName("UTF-8")));
+
+        String lastLine = null;
+        String line;
+        while ((line = br.readLine()) != null) {
+            // read the response and remember the last line
+            lastLine = line;
+        }
+        pw.close();
+        br.close();
+
+        return lastLine;
+    }
+}
+
+
+
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/VariantsTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/VariantsTest.java
new file mode 100644
index 0000000..b6c0f74
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/VariantsTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2010, 2018 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.api;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Server-side variant selection & handling test.
+ *
+ * @author Paul Sandoz
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class VariantsTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(
+                LanguageVariantResource.class,
+                ComplexVariantResource.class,
+                MediaTypeQualitySourceResource.class);
+    }
+
+    @Path("/lvr")
+    public static class LanguageVariantResource {
+
+        @GET
+        public Response doGet(@Context Request r) {
+            List<Variant> vs = Variant.VariantListBuilder.newInstance()
+                    .languages(new Locale("zh"))
+                    .languages(new Locale("fr"))
+                    .languages(new Locale("en"))
+                    .add()
+                    .build();
+
+            Variant v = r.selectVariant(vs);
+            if (v == null) {
+                return Response.notAcceptable(vs).build();
+            } else {
+                return Response.ok(v.getLanguage().toString(), v).build();
+            }
+        }
+    }
+
+    @Test
+    public void testGetLanguageEn() throws IOException {
+        WebTarget rp = target("/lvr");
+
+        Response r = rp.request()
+                .header("Accept-Language", "en")
+                .get();
+        assertEquals("en", r.readEntity(String.class));
+        assertEquals("en", r.getLanguage().toString());
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(!contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+    }
+
+    @Test
+    public void testGetLanguageZh() throws IOException {
+        WebTarget rp = target("/lvr");
+
+        Response r = rp.request()
+                .header("Accept-Language", "zh")
+                .get();
+        assertEquals("zh", r.readEntity(String.class));
+        assertEquals("zh", r.getLanguage().toString());
+        System.out.println(r.getMetadata().getFirst("Vary"));
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(!contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+    }
+
+    @Test
+    public void testGetLanguageMultiple() throws IOException {
+        WebTarget rp = target("/lvr");
+
+        Response r = rp
+                .request()
+                .header("Accept-Language", "en;q=0.3, zh;q=0.4, fr")
+                .get();
+        assertEquals("fr", r.readEntity(String.class));
+        assertEquals("fr", r.getLanguage().toString());
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(!contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+    }
+
+    @Path("/cvr")
+    public static class ComplexVariantResource {
+
+        @GET
+        public Response doGet(@Context Request r) {
+            List<Variant> vs = Variant.VariantListBuilder.newInstance()
+                    .mediaTypes(MediaType.valueOf("image/jpeg"))
+                    .add()
+                    .mediaTypes(MediaType.valueOf("application/xml"))
+                    .languages(new Locale("en", "us"))
+                    .add()
+                    .mediaTypes(MediaType.valueOf("text/xml"))
+                    .languages(new Locale("en"))
+                    .add()
+                    .mediaTypes(MediaType.valueOf("text/xml"))
+                    .languages(new Locale("en", "us"))
+                    .add()
+                    .build();
+
+            Variant v = r.selectVariant(vs);
+            if (v == null) {
+                return Response.notAcceptable(vs).build();
+            } else {
+                return Response.ok("GET", v).build();
+            }
+        }
+    }
+
+    @Test
+    public void testGetComplex1() throws IOException {
+        WebTarget rp = target("/cvr");
+
+        Response r = rp.request("text/xml",
+                "application/xml",
+                "application/xhtml+xml",
+                "image/png",
+                "text/html;q=0.9",
+                "text/plain;q=0.8",
+                "*/*;q=0.5")
+                .header("Accept-Language", "en-us,en;q=0.5")
+                .get();
+        assertEquals("GET", r.readEntity(String.class));
+        assertEquals(MediaType.valueOf("text/xml"), r.getMediaType());
+        assertEquals("en", r.getLanguage().getLanguage());
+        assertEquals("US", r.getLanguage().getCountry());
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+    }
+
+    @Test
+    public void testGetComplex2() throws IOException {
+        WebTarget rp = target("/cvr");
+
+        Response r = rp.request("text/xml",
+                "application/xml",
+                "application/xhtml+xml",
+                "image/png",
+                "text/html;q=0.9",
+                "text/plain;q=0.8",
+                "*/*;q=0.5")
+                .header("Accept-Language", "en,en-us")
+                .get();
+        assertEquals("GET", r.readEntity(String.class));
+        assertEquals(MediaType.valueOf("text/xml"), r.getMediaType());
+        assertEquals("en", r.getLanguage().toString());
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+    }
+
+    @Test
+    public void testGetComplex3() throws IOException {
+        WebTarget rp = target("/cvr");
+
+        Response r = rp.request("application/xml",
+                "text/xml",
+                "application/xhtml+xml",
+                "image/png",
+                "text/html;q=0.9",
+                "text/plain;q=0.8",
+                "*/*;q=0.5")
+                .header("Accept-Language", "en-us,en;q=0.5")
+                .get();
+        assertEquals("GET", r.readEntity(String.class));
+        assertEquals(MediaType.valueOf("application/xml"), r.getMediaType());
+        assertEquals("en", r.getLanguage().getLanguage());
+        assertEquals("US", r.getLanguage().getCountry());
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+    }
+
+    @Test
+    public void testGetComplexNotAcceptable() throws IOException {
+        WebTarget rp = target("/cvr");
+
+        Response r = rp.request("application/atom+xml")
+                .header("Accept-Language", "en-us,en")
+                .get();
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+        assertEquals(406, r.getStatus());
+
+        r = rp.request("application/xml")
+                .header("Accept-Language", "fr")
+                .get();
+        vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+        assertTrue(contains(vary, "Accept-Language"));
+        assertEquals(406, r.getStatus());
+    }
+
+    @Path("/mtqsr")
+    public static class MediaTypeQualitySourceResource {
+
+        @GET
+        public Response doGet(@Context Request r) {
+            List<Variant> vs = Variant.VariantListBuilder.newInstance()
+                    .mediaTypes(MediaType.valueOf("application/xml;qs=0.8"))
+                    .mediaTypes(MediaType.valueOf("text/html;qs=1.0"))
+                    .add()
+                    .build();
+
+            Variant v = r.selectVariant(vs);
+            if (v == null) {
+                return Response.notAcceptable(vs).build();
+            } else {
+                return Response.ok("GET", v).build();
+            }
+        }
+    }
+
+    @Test
+    public void testMediaTypeQualitySource() throws IOException {
+        WebTarget rp = target("/mtqsr");
+
+        Response r = rp.request(
+                "application/xml",
+                "text/html;q=0.9")
+                .get();
+        assertTrue(MediaTypes.typeEqual(MediaType.valueOf("text/html"), r.getMediaType()));
+        String vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+
+        r = rp.request(
+                "text/html",
+                "application/xml")
+                .get();
+        assertTrue(MediaTypes.typeEqual(MediaType.valueOf("text/html"), r.getMediaType()));
+        vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+
+        r = rp.request(
+                "application/xml",
+                "text/html")
+                .get();
+        assertTrue(MediaTypes.typeEqual(MediaType.valueOf("text/html"), r.getMediaType()));
+        vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+
+        r = rp.request(
+                "application/xml")
+                .get();
+        assertTrue(MediaTypes.typeEqual(MediaType.valueOf("application/xml"), r.getMediaType()));
+        vary = r.getHeaderString("Vary");
+        assertNotNull(vary);
+        assertTrue(contains(vary, "Accept"));
+    }
+
+    private boolean contains(String l, String v) {
+        String[] vs = l.split(",");
+        for (String s : vs) {
+            s = s.trim();
+            if (s.equalsIgnoreCase(v)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java
new file mode 100644
index 0000000..9fedb4d
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ClientTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012, 2018 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;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ClientTest extends JerseyTest {
+
+    @Path("helloworld")
+    @Produces(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.TEXT_PLAIN)
+    public static class HelloWorldResource {
+
+        private static final String MESSAGE = "Hello world!";
+
+        @GET
+        public String getClichedMessage() {
+            return MESSAGE;
+        }
+    }
+
+    @Path("headers")
+    @Produces(MediaType.TEXT_PLAIN)
+    public static class HeadersTestResource {
+
+        @POST
+        @Path("content")
+        public String contentHeaders(@HeaderParam("custom-header") final String customHeader,
+                                     @Context final HttpHeaders headers, final String entity) {
+            final StringBuilder sb = new StringBuilder(entity).append('\n');
+
+            sb.append("custom-header:").append(customHeader).append('\n');
+
+            for (final Map.Entry<String, List<String>> header : headers.getRequestHeaders().entrySet()) {
+                sb.append(header.getKey()).append(':').append(header.getValue().toString()).append('\n');
+            }
+
+            return sb.toString();
+        }
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class, HeadersTestResource.class);
+    }
+
+    @Test
+    public void testAccesingHelloworldResource() {
+        final WebTarget resource = target().path("helloworld");
+        final Response r = resource.request().get();
+        assertEquals(200, r.getStatus());
+
+        final String responseMessage = resource.request().get(String.class);
+        assertEquals(HelloWorldResource.MESSAGE, responseMessage);
+    }
+
+    @Test
+    public void testAccesingMissingResource() {
+        final WebTarget missingResource = target().path("missing");
+        final Response r = missingResource.request().get();
+        assertEquals(404, r.getStatus());
+
+
+        try {
+            missingResource.request().get(String.class);
+        } catch (final WebApplicationException ex) {
+            assertEquals(404, ex.getResponse().getStatus());
+            return;
+        }
+
+        fail("Expected WebApplicationException has not been thrown.");
+    }
+
+    @Test
+    // Inspired by JERSEY-1502
+    public void testContextHeaders() {
+        final WebTarget target = target().path("headers").path("content");
+
+        Invocation.Builder ib;
+        Invocation i;
+        Response r;
+        String reqHeaders;
+
+        ib = target.request("*/*");
+        ib.header("custom-header", "custom-value");
+        ib.header("content-encoding", "deflate");
+        i = ib.build("POST", Entity.entity("aaa", MediaType.WILDCARD_TYPE));
+        r = i.invoke();
+
+        reqHeaders = r.readEntity(String.class).toLowerCase();
+        for (final String expected : new String[] {"custom-header:[custom-value]", "custom-header:custom-value"}) {
+            assertTrue(String.format("Request headers do not contain expected '%s' entry:\n%s", expected, reqHeaders),
+                    reqHeaders.contains(expected));
+        }
+        final String unexpected = "content-encoding";
+        assertFalse(String.format("Request headers contains unexpected '%s' entry:\n%s", unexpected, reqHeaders),
+                reqHeaders.contains(unexpected));
+
+        ib = target.request("*/*");
+        i = ib.build("POST",
+                Entity.entity("aaa", Variant.mediaTypes(MediaType.WILDCARD_TYPE).encodings("deflate").build().get(0)));
+        r = i.invoke();
+
+        final String expected = "content-encoding:[deflate]";
+        reqHeaders = r.readEntity(String.class).toLowerCase();
+        assertTrue(String.format("Request headers do not contain expected '%s' entry:\n%s", expected, reqHeaders),
+                reqHeaders.contains(expected));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ExecutorServiceProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ExecutorServiceProviderTest.java
new file mode 100644
index 0000000..c97f574
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/ExecutorServiceProviderTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2013, 2018 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;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientAsyncExecutor;
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.server.ManagedAsync;
+import org.glassfish.jersey.server.ManagedAsyncExecutor;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.spi.ExecutorServiceProvider;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * {@link org.glassfish.jersey.spi.ExecutorServiceProvider} E2E tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ExecutorServiceProviderTest extends JerseyTest {
+
+    @Path("resource")
+    @Produces("text/plain")
+    public static class TestResourceA {
+
+        @GET
+        public String getSync() {
+            return "resource";
+        }
+
+        @GET
+        @Path("async")
+        @ManagedAsync
+        public String getAsync() {
+            return "async-resource-" + ExecutorServiceProviderTest.getResponseOnThread();
+        }
+    }
+
+    // A separate resource class for the custom-async sub-path to ensure that
+    // the named ExecutorService injection is only performed for the "/resource/custom-async" path.
+    @Path("resource")
+    @Produces("text/plain")
+    public static class TestResourceB {
+
+        @Inject
+        @Named("custom")
+        ExecutorService executorService;
+
+        @GET
+        @Path("custom-async")
+        public void getCustomAsync(@Suspended final AsyncResponse asyncResponse) {
+            executorService.submit(new Runnable() {
+                @Override
+                public void run() {
+                    asyncResponse.resume("custom-async-resource-" + ExecutorServiceProviderTest.getResponseOnThread());
+                }
+            });
+        }
+    }
+
+    static String getResponseOnThread() {
+        final String threadName = Thread.currentThread().getName();
+        if (threadName.startsWith("async-request-")) {
+            return "passed";
+        } else {
+            return "error - unexpected custom thread name: " + threadName;
+        }
+    }
+
+    @ClientAsyncExecutor
+    @ManagedAsyncExecutor
+    @Named("custom")
+    public static class CustomExecutorProvider implements ExecutorServiceProvider {
+
+        private final Set<ExecutorService> executors = Collections.newSetFromMap(new IdentityHashMap<>());
+        private volatile int executorCreationCount = 0;
+        private volatile int executorReleaseCount = 0;
+
+        public void reset() {
+            for (ExecutorService executor : executors) {
+                executor.shutdownNow();
+            }
+
+            executors.clear();
+            executorCreationCount = 0;
+            executorReleaseCount = 0;
+        }
+
+        @Override
+        public ExecutorService getExecutorService() {
+            return new CustomExecutorService();
+        }
+
+        @Override
+        public void dispose(final ExecutorService executorService) {
+            executorService.shutdownNow();
+        }
+
+        private class CustomExecutorService implements ExecutorService {
+
+            private final ExecutorService delegate;
+            private final AtomicBoolean isCleanedUp;
+
+            public CustomExecutorService() {
+                this.isCleanedUp = new AtomicBoolean(false);
+                this.delegate = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
+                        .setNameFormat("async-request-%d")
+                        .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                        .build());
+
+                executorCreationCount++;
+                executors.add(this);
+            }
+
+            @Override
+            public void shutdown() {
+                tryCleanUp();
+                delegate.shutdown();
+            }
+
+            @Override
+            public List<Runnable> shutdownNow() {
+                tryCleanUp();
+                return delegate.shutdownNow();
+            }
+
+            @Override
+            public boolean isShutdown() {
+                return delegate.isShutdown();
+            }
+
+            @Override
+            public boolean isTerminated() {
+                return delegate.isTerminated();
+            }
+
+            @Override
+            public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+                return delegate.awaitTermination(timeout, unit);
+            }
+
+            @Override
+            public <T> Future<T> submit(Callable<T> task) {
+                return delegate.submit(task);
+            }
+
+            @Override
+            public <T> Future<T> submit(Runnable task, T result) {
+                return delegate.submit(task, result);
+            }
+
+            @Override
+            public Future<?> submit(Runnable task) {
+                return delegate.submit(task);
+            }
+
+            @Override
+            public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+                return delegate.invokeAll(tasks);
+            }
+
+            @Override
+            public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+                    throws InterruptedException {
+                return delegate.invokeAll(tasks, timeout, unit);
+            }
+
+            @Override
+            public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+                return delegate.invokeAny(tasks);
+            }
+
+            @Override
+            public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+                    throws InterruptedException, ExecutionException, TimeoutException {
+                return delegate.invokeAny(tasks, timeout, unit);
+            }
+
+            private void tryCleanUp() {
+                if (isCleanedUp.compareAndSet(false, true)) {
+                    executors.remove(this);
+                    executorReleaseCount++;
+                }
+            }
+
+            @Override
+            public void execute(Runnable command) {
+                delegate.execute(command);
+            }
+        }
+    }
+
+    private static final CustomExecutorProvider serverExecutorProvider = new CustomExecutorProvider();
+
+    @Override
+    protected Application configure() {
+        // enable(TestProperties.LOG_TRAFFIC);
+        // enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig(TestResourceA.class, TestResourceB.class).register(serverExecutorProvider);
+    }
+
+    /**
+     * Reproducer for JERSEY-2205 (client-side).
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testCustomClientExecutorsInjectionAndReleasing() throws Exception {
+        final CustomExecutorProvider provider = new CustomExecutorProvider();
+        Client client = ClientBuilder.newClient().register(provider);
+
+        Response response = client.target(getBaseUri()).path("resource").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("resource", response.readEntity(String.class));
+
+        // no executors should be created or released at this point yet
+        assertEquals("Unexpected number of created client executors", 0, provider.executorCreationCount);
+        assertEquals("Unexpected number of released client executors", 0, provider.executorReleaseCount);
+        assertEquals("Unexpected number of client executors stored in the set.",
+                0, provider.executors.size());
+
+        Future<Response> fr = client.target(getBaseUri()).path("resource").request().async().get();
+        response = fr.get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("resource", response.readEntity(String.class));
+
+        // single executor should be created but not released at this point yet
+        assertEquals("Unexpected number of created client executors", 1, provider.executorCreationCount);
+        assertEquals("Unexpected number of released client executors", 0, provider.executorReleaseCount);
+        assertEquals("Unexpected number of client executors stored in the set.",
+                1, provider.executors.size());
+
+        client.close();
+
+        // the created executor needs to be released by now; no more executors should be created
+        assertEquals("Unexpected number of created client executors", 1, provider.executorCreationCount);
+        assertEquals("Unexpected number of released client executors", 1, provider.executorReleaseCount);
+        assertEquals("Unexpected number of client executors stored in the set.",
+                0, provider.executors.size());
+    }
+
+    /**
+     * Reproducer for JERSEY-2205 (server-side).
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testCustomServerExecutorsInjectionAndReleasing() throws Exception {
+        // reset server executor statistics to avoid data pollution from other test methods
+        serverExecutorProvider.reset();
+
+        Response response = target("resource").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("resource", response.readEntity(String.class));
+
+        // no executors should be created or released at this point yet
+        assertEquals("Unexpected number of created server executors", 0, serverExecutorProvider.executorCreationCount);
+        assertEquals("Unexpected number of released server executors", 0, serverExecutorProvider.executorReleaseCount);
+        assertEquals("Unexpected number of server executors stored in the set.",
+                0, serverExecutorProvider.executors.size());
+
+        response = target("resource/async").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("async-resource-passed", response.readEntity(String.class));
+
+        // single executor should be created but not released at this point yet
+        assertEquals("Unexpected number of created server executors", 1, serverExecutorProvider.executorCreationCount);
+        assertEquals("Unexpected number of released server executors", 0, serverExecutorProvider.executorReleaseCount);
+        assertEquals("Unexpected number of server executors stored in the set.",
+                1, serverExecutorProvider.executors.size());
+
+        tearDown(); // stopping test container
+
+        // the created executor needs to be released by now; no more executors should be created
+        assertEquals("Unexpected number of created server executors", 1, serverExecutorProvider.executorCreationCount);
+        assertEquals("Unexpected number of released server executors", 1, serverExecutorProvider.executorReleaseCount);
+        assertEquals("Unexpected number of server executors stored in the set.",
+                0, serverExecutorProvider.executors.size());
+
+        setUp(); // re-starting test container to ensure proper post-test tearDown.
+    }
+
+    /**
+     * Test named custom executor injection and release mechanism.
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testCustomNamedServerExecutorsInjectionAndReleasing() throws Exception {
+        serverExecutorProvider.reset();
+
+        Response response = target("resource/custom-async").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("custom-async-resource-passed", response.readEntity(String.class));
+
+        // single executor should be created but not released at this point yet
+        assertEquals("Unexpected number of created server executors", 1, serverExecutorProvider.executorCreationCount);
+        assertEquals("Unexpected number of released server executors", 0, serverExecutorProvider.executorReleaseCount);
+        assertEquals("Unexpected number of server executors stored in the set.",
+                1, serverExecutorProvider.executors.size());
+
+        tearDown(); // stopping test container
+
+        // the created executor needs to be released by now; no more executors should be created
+        assertEquals("Unexpected number of created server executors", 1, serverExecutorProvider.executorCreationCount);
+        assertEquals("Unexpected number of released server executors", 1, serverExecutorProvider.executorReleaseCount);
+        assertEquals("Unexpected number of server executors stored in the set.",
+                0, serverExecutorProvider.executors.size());
+
+        setUp(); // re-starting test container to ensure proper post-test tearDown.
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/InterceptorCustomTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/InterceptorCustomTest.java
new file mode 100644
index 0000000..b2aac95
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/InterceptorCustomTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2012, 2018 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.InterceptorContext;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.tests.e2e.InterceptorGzipTest.GZIPReaderTestInterceptor;
+import org.glassfish.jersey.tests.e2e.InterceptorGzipTest.GZIPWriterTestInterceptor;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests interceptors.
+ *
+ * @author Miroslav Fuksa
+ * @author Michal Gajdos
+ */
+public class InterceptorCustomTest extends JerseyTest {
+
+    private static final String FROM_RESOURCE = "-from_resource";
+    private static final String ENTITY = "hello, this is text entity";
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(TestResource.class, GZIPWriterTestInterceptor.class, GZIPReaderTestInterceptor.class,
+                PlusOneWriterInterceptor.class, MinusOneReaderInterceptor.class, AnnotationsReaderWriterInterceptor.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(GZIPReaderTestInterceptor.class)
+                .register(GZIPWriterTestInterceptor.class)
+                .register(PlusOneWriterInterceptor.class)
+                .register(MinusOneReaderInterceptor.class)
+                .register(AnnotationsReaderWriterInterceptor.class);
+    }
+
+    @Test
+    public void testMoreInterceptorsAndFilter() throws IOException {
+        WebTarget target = target().path("test");
+
+        Response response = target.request().put(Entity.entity(ENTITY, MediaType.TEXT_PLAIN_TYPE));
+        String str = response.readEntity(String.class);
+        assertEquals(ENTITY + FROM_RESOURCE, str);
+    }
+
+    @Path("test")
+    public static class TestResource {
+
+        @PUT
+        @Produces("text/plain")
+        @Consumes("text/plain")
+        public String put(String str) {
+            System.out.println("resource: " + str);
+            return str + FROM_RESOURCE;
+        }
+    }
+
+    /**
+     * Interceptor which adds +1 to each byte written into the stream.
+     */
+    @Provider
+    @Priority(300)
+    public static class PlusOneWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final OutputStream old = context.getOutputStream();
+            context.setOutputStream(new OutputStream() {
+
+                @Override
+                public void write(int b) throws IOException {
+                    if (b == 255) {
+                        old.write(0);
+                    } else {
+                        old.write(b + 1);
+                    }
+                }
+            });
+            context.proceed();
+        }
+    }
+
+    /**
+     * Interceptor which adds +1 to each byte read from the stream.
+     */
+    @Provider
+    @Priority(300)
+    public static class MinusOneReaderInterceptor implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final InputStream old = context.getInputStream();
+            context.setInputStream(new InputStream() {
+
+                @Override
+                public int read() throws IOException {
+                    int b = old.read();
+                    if (b == -1) {
+                        return -1;
+                    } else if (b == 0) {
+                        return 255;
+                    } else {
+                        return b - 1;
+                    }
+                }
+            });
+
+            return context.proceed();
+        }
+    }
+
+    /**
+     * Interceptor that tries to set annotations of {@link InterceptorContext} to {@code null},
+     * empty array and finally to the original value.
+     */
+    @Provider
+    @Priority(300)
+    public static class AnnotationsReaderWriterInterceptor implements ReaderInterceptor, WriterInterceptor {
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final Annotation[] annotations = context.getAnnotations();
+
+            // Fails if no NPE is thrown.
+            unsetAnnotations(context);
+            // Ok.
+            setAnnotations(context, new Annotation[0]);
+            // Ok.
+            setAnnotations(context, annotations);
+
+            return context.proceed();
+        }
+
+        @Override
+        public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final Annotation[] annotations = context.getAnnotations();
+
+            // Fails if no NPE is thrown.
+            unsetAnnotations(context);
+            // Ok.
+            setAnnotations(context, new Annotation[0]);
+            // Ok.
+            setAnnotations(context, annotations);
+
+            context.proceed();
+        }
+
+        private void setAnnotations(final InterceptorContext context, final Annotation[] annotations) {
+            if (context.getAnnotations() != null) {
+                context.setAnnotations(annotations);
+            }
+        }
+
+        private void unsetAnnotations(final InterceptorContext context) {
+            try {
+                context.setAnnotations(null);
+
+                fail("NullPointerException expected.");
+            } catch (NullPointerException npe) {
+                // OK.
+            }
+        }
+    }
+
+    @Provider
+    @Priority(100)
+    public static class IOExceptionReaderInterceptor implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            throw new IOException("client io");
+        }
+    }
+
+    @Test
+    public void testIOException() throws IOException {
+        client().register(IOExceptionReaderInterceptor.class);
+
+        WebTarget target = target().path("test");
+
+        Response response = target.request().put(Entity.entity(ENTITY, MediaType.TEXT_PLAIN_TYPE));
+
+        try {
+            response.readEntity(String.class);
+            fail("ProcessingException expected.");
+        } catch (ProcessingException e) {
+            assertTrue(e.getCause() instanceof IOException);
+        }
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/InterceptorGzipTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/InterceptorGzipTest.java
new file mode 100644
index 0000000..96475ab
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/InterceptorGzipTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2012, 2018 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;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests gzip interceptors.
+ *
+ * @author Miroslav Fuksa
+ *
+ */
+public class InterceptorGzipTest extends JerseyTest {
+
+    private static final String FROM_RESOURCE = "-from_resource";
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(TestResource.class, GZIPWriterTestInterceptor.class,
+                GZIPReaderTestInterceptor.class); // GZIPInterceptor.class
+    }
+
+    @Test
+    public void testGzipInterceptorOnlyOnServer() throws IOException {
+        client().register(GZIPWriterTestInterceptor.class);
+        WebTarget target = target().path("test");
+        String entity = "hello, this is text entity";
+        Response response = target.request().put(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE));
+        InputStream is = response.readEntity(InputStream.class);
+        GZIPInputStream gzipIs = new GZIPInputStream(is);
+        BufferedReader br = new BufferedReader(new InputStreamReader(gzipIs));
+        String str = br.readLine();
+        assertEquals(entity + FROM_RESOURCE, str);
+    }
+
+    @Test
+    public void testGzipInterceptorOnServerandClient() throws IOException {
+        client().register(GZIPReaderTestInterceptor.class).register(GZIPWriterTestInterceptor.class);
+        WebTarget target = target().path("test");
+        String entity = "hello, this is text entity";
+        Response response = target.request().put(Entity.entity(entity, MediaType.TEXT_PLAIN_TYPE));
+        String str = response.readEntity(String.class);
+        assertEquals(entity + FROM_RESOURCE, str);
+    }
+
+    @Path("test")
+    public static class TestResource {
+
+        @PUT
+        @Produces("text/plain")
+        @Consumes("text/plain")
+        public String put(String str) {
+            System.out.println("resource: " + str);
+            return str + FROM_RESOURCE;
+        }
+    }
+
+    @Provider
+    @Priority(200)
+    public static class CustomWriterInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final OutputStream old = context.getOutputStream();
+            OutputStream newOut = new OutputStream() {
+
+                @Override
+                public void write(int b) throws IOException {
+                    if (b == Byte.MAX_VALUE) {
+                        old.write(Byte.MIN_VALUE);
+                    } else {
+                        old.write(b + 1);
+                    }
+                }
+            };
+            context.setOutputStream(newOut);
+            try {
+                context.proceed();
+            } finally {
+                context.setOutputStream(old);
+            }
+        }
+    }
+
+    @Provider
+    @Priority(200)
+    public static class GZIPWriterTestInterceptor implements WriterInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            OutputStream old = context.getOutputStream();
+            GZIPOutputStream newStream = new GZIPOutputStream(old);
+            context.setOutputStream(newStream);
+            if (context.getHeaders().containsKey(HttpHeaders.CONTENT_LENGTH)) {
+                List<Object> clen = new ArrayList<>();
+                clen.add(-1L);
+                context.getHeaders().put(HttpHeaders.CONTENT_LENGTH, clen);
+            }
+            try {
+                context.proceed();
+            } finally {
+                newStream.finish();
+                context.setOutputStream(old);
+            }
+        }
+    }
+
+    @Provider
+    @Priority(200)
+    public static class GZIPReaderTestInterceptor implements ReaderInterceptor {
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            InputStream old = context.getInputStream();
+            context.setInputStream(new GZIPInputStream(old));
+            try {
+                return context.proceed();
+            } finally {
+                context.setInputStream(old);
+            }
+        }
+
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/LinkTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/LinkTest.java
new file mode 100644
index 0000000..e7033ed
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/LinkTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2012, 2018 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;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.uri.UriTemplate;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Martin Matula
+ */
+public class LinkTest extends JerseyTest {
+
+    @Path("resource")
+    public static class Resource {
+
+        @POST
+        @Produces({
+                MediaType.APPLICATION_XHTML_XML,
+                MediaType.APPLICATION_ATOM_XML,
+                MediaType.APPLICATION_SVG_XML
+        })
+        @Path("producesxml")
+        public String producesXml() {
+            return "";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, LinkTestResource.class);
+    }
+
+    @Test
+    public void testEquals() {
+        Link link = Link.fromMethod(Resource.class, "producesXml").build();
+        String string = link.toString();
+        Link fromValueOf = Link.valueOf(string);
+        assertEquals(link, fromValueOf);
+    }
+
+    @Test
+    public void testFromResourceMethod() {
+        Link link = Link.fromMethod(Resource.class, "producesXml").build();
+        assertEquals("producesxml", link.getUri().toString());
+    }
+
+    @Test
+    public void testDelimiters() {
+        Link.Builder builder = Link.fromUri("http://localhost:80");
+        final String value = "param1value1    param1value2";
+        builder = builder.param("param1", value);
+        Link link = builder.build();
+        final Map<String, String> params = link.getParams();
+        assertEquals(value, params.get("param1"));
+    }
+
+    @Path("linktest")
+    public static class LinkTestResource {
+
+        @GET
+        public Response get(@Context UriInfo uriInfo) throws Exception {
+            URI test1 = URI.create(uriInfo.getAbsolutePath().toString() + "test1");
+            URI test2 = URI.create(uriInfo.getAbsolutePath().toString() + "test2");
+
+            return Response.ok()
+                    .link("http://oracle.com", "parent")
+                    .link(new URI("http://jersey.java.net"), "framework")
+                    .links(
+                            Link.fromUri(uriInfo.relativize(test1)).rel("test1").build(),
+                            Link.fromUri(test2).rel("test2").build(),
+                            Link.fromUri(uriInfo.relativize(URI.create("linktest/test3"))).rel("test3").build()
+                    ).build();
+        }
+    }
+
+    @Test
+    public void simpleLinkTest() {
+        final WebTarget target = target("/linktest/");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+
+        final URI targetUri = target.getUri();
+        assertThat(response.getLink("parent").getUri(), equalTo(URI.create("http://oracle.com")));
+        assertThat(response.getLink("framework").getUri(), equalTo(URI.create("http://jersey.java.net")));
+
+        assertThat(response.getLink("test1").getUri(), equalTo(UriTemplate.resolve(targetUri, "test1")));
+        assertThat(response.getLink("test2").getUri(), equalTo(UriTemplate.resolve(targetUri, "test2")));
+        assertThat(response.getLink("test3").getUri(), equalTo(UriTemplate.resolve(targetUri, "test3")));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/MessageBodyExceptionWrappingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/MessageBodyExceptionWrappingTest.java
new file mode 100644
index 0000000..2b5d2e6
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/MessageBodyExceptionWrappingTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2012, 2018 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import javax.xml.transform.stream.StreamSource;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import static javax.ws.rs.client.Entity.entity;
+
+/**
+ * @author Miroslav Fuksa
+ */
+public class MessageBodyExceptionWrappingTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    /**
+     * Tests whether fail of message body writer causes throwing exception. Previously the
+     * exception was not thrown and 500 status code was returned in the response.
+     */
+    @Test
+    public void testWrapping() {
+        WebTarget resource = target().path("test");
+        StreamSource source = new StreamSource() {
+            @Override
+            public InputStream getInputStream() {
+                throw new WebApplicationException(555);
+            }
+        };
+        try {
+            Response response = resource.request().post(Entity.entity(source, MediaType.TEXT_XML_TYPE));
+            fail("Exception expected, instead response with " + response.getStatus() + " status has been returned.");
+        } catch (ProcessingException e) {
+            assertEquals(WebApplicationException.class, e.getCause().getClass());
+            assertEquals(555, ((WebApplicationException) e.getCause()).getResponse().getStatus());
+        }
+    }
+
+    @Path("test")
+    public static class TestResource {
+
+        @POST
+        public String echo(String entity) {
+            return entity;
+        }
+    }
+
+    /**
+     * Provider reproducing JERSEY-1990.
+     */
+    @Produces("text/foo")
+    public static class ThrowingUpProvider implements MessageBodyWriter<String>, MessageBodyReader<String> {
+        public volatile int counter = 0;
+
+        public int getInvocationCount() {
+            return counter;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            counter++;
+            final RuntimeException up = new RuntimeException("lunch");
+            throw up;
+        }
+
+        @Override
+        public long getSize(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            throw new UnsupportedOperationException("This method should not have ever been called.");
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            counter++;
+            final RuntimeException up = new RuntimeException("dinner");
+            throw up;
+        }
+
+        @Override
+        public String readFrom(Class<String> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                               MultivaluedMap<String, String> httpHeaders,
+                               InputStream entityStream) throws IOException, WebApplicationException {
+            throw new UnsupportedOperationException("This method should not have ever been called.");
+        }
+    }
+
+    /**
+     * Reproducer for JERSEY-1990.
+     */
+    @Test
+    public void testMbwHandlingExceptionInIsReadableWritable() {
+        ThrowingUpProvider throwingUpProvider = new ThrowingUpProvider();
+
+        final String response =
+                target("test").register(throwingUpProvider).request("text/foo").post(entity("hello", "text/foo"), String.class);
+
+        assertEquals("hello", response);
+        assertEquals(2, throwingUpProvider.getInvocationCount());
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HugeEntityTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HugeEntityTest.java
new file mode 100644
index 0000000..3f6e0a4
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/HugeEntityTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2014, 2018 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.client.connector;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test to make sure huge entity gets chunk-encoded.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class HugeEntityTest extends JerseyTest {
+
+    private static final int BUFFER_LENGTH = 1024 * 1024; // 1M
+    private static final long HUGE_DATA_LENGTH = 20L * 1024L * 1024L * 1024L; // 20G seems sufficient
+
+    /**
+     * JERSEY-2337 reproducer. The resource is used to check the right amount of data
+     * is being received from the client and also gives us ability to check we receive
+     * correct data.
+     */
+    @Path("/")
+    public static class ConsumerResource {
+
+        /**
+         * Return back the count of bytes received.
+         * This way, we should be able to consume a huge amount of data.
+         */
+        @POST
+        @Path("size")
+        public String post(InputStream in) throws IOException {
+
+            long totalBytesRead = 0L;
+
+            byte[] buffer = new byte[BUFFER_LENGTH];
+            int read;
+            do {
+                read = in.read(buffer);
+                if (read > 0) {
+                    totalBytesRead += read;
+                }
+            } while (read != -1);
+
+            return String.valueOf(totalBytesRead);
+        }
+
+        @POST
+        @Path("echo")
+        public String echo(String s) {
+            return s;
+        }
+    }
+
+    private final ConnectorProvider connectorProvider;
+
+    public HugeEntityTest(ConnectorProvider connectorProvider) {
+        this.connectorProvider = connectorProvider;
+    }
+
+    @Parameterized.Parameters
+    public static List<? extends ConnectorProvider> testData() {
+        return Arrays.asList(new GrizzlyConnectorProvider(), new JdkConnectorProvider());
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ConsumerResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(TestEntityWriter.class);
+        config.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
+        config.connectorProvider(connectorProvider);
+    }
+
+    public static class TestEntity {
+
+        final long size;
+
+        public TestEntity(long size) {
+            this.size = size;
+        }
+    }
+
+    /**
+     * Utility writer that generates that many zero bytes as given by the input entity size field.
+     */
+    public static class TestEntityWriter implements MessageBodyWriter<TestEntity> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == TestEntity.class;
+        }
+
+        @Override
+        public long getSize(TestEntity t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1; // no matter what we return here, the output will get chunk-encoded
+        }
+
+        @Override
+        public void writeTo(TestEntity t,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+
+            final byte[] buffer = new byte[BUFFER_LENGTH];
+            final long bufferCount = t.size / BUFFER_LENGTH;
+            final int remainder = (int) (t.size % BUFFER_LENGTH);
+
+            for (long b = 0; b < bufferCount; b++) {
+                entityStream.write(buffer);
+            }
+
+            if (remainder > 0) {
+                entityStream.write(buffer, 0, remainder);
+            }
+        }
+    }
+
+    /**
+     * JERSEY-2337 reproducer. We are going to send huge amount of data over the wire.
+     * Should not the data have been chunk-encoded, we would easily run out of memory.
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testPost() throws Exception {
+        Response response = target("/size").request()
+                .post(Entity.entity(new TestEntity(HUGE_DATA_LENGTH), MediaType.APPLICATION_OCTET_STREAM_TYPE));
+        String content = response.readEntity(String.class);
+        assertThat(Long.parseLong(content), equalTo(HUGE_DATA_LENGTH));
+
+        // just to check the right data have been transfered.
+        response = target("/echo").request().post(Entity.text("Hey Sync!"));
+        assertThat(response.readEntity(String.class), equalTo("Hey Sync!"));
+    }
+
+    /**
+     * JERSEY-2337 reproducer. We are going to send huge amount of data over the wire. This time in an async fashion.
+     * Should not the data have been chunk-encoded, we would easily run out of memory.
+     *
+     * @throws Exception in case of a test error.
+     */
+    @Test
+    public void testAsyncPost() throws Exception {
+        Future<Response> response = target("/size").request().async()
+                .post(Entity.entity(new TestEntity(HUGE_DATA_LENGTH), MediaType.APPLICATION_OCTET_STREAM_TYPE));
+        final String content = response.get().readEntity(String.class);
+        assertThat(Long.parseLong(content), equalTo(HUGE_DATA_LENGTH));
+
+        // just to check the right data have been transfered.
+        response = target("/echo").request().async().post(Entity.text("Hey Async!"));
+        assertThat(response.get().readEntity(String.class), equalTo("Hey Async!"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MethodTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MethodTest.java
new file mode 100644
index 0000000..c3d2d0e
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/MethodTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010, 2018 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.client.connector;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the Http methods.
+ *
+ * @author Stepan Kopriva
+ */
+@RunWith(Parameterized.class)
+public class MethodTest extends JerseyTest {
+
+    private static final String PATH = "test";
+
+    @Path("/test")
+    public static class HttpMethodResource {
+
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @POST
+        public String post(String entity) {
+            return entity;
+        }
+
+        @PUT
+        public String put(String entity) {
+            return entity;
+        }
+
+        @DELETE
+        public String delete() {
+            return "DELETE";
+        }
+    }
+
+    private final ConnectorProvider connectorProvider;
+
+    public MethodTest(ConnectorProvider connectorProvider) {
+        this.connectorProvider = connectorProvider;
+    }
+
+    @Parameterized.Parameters
+    public static List<? extends ConnectorProvider> testData() {
+        return Arrays.asList(new GrizzlyConnectorProvider(), new JdkConnectorProvider());
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HttpMethodResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(connectorProvider);
+    }
+
+    @Test
+    public void testGet() {
+        Response response = target(PATH).request().get();
+        assertEquals("GET", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPost() {
+        Response response = target(PATH).request().post(Entity.entity("POST", MediaType.TEXT_PLAIN));
+        assertEquals("POST", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testPut() {
+        Response response = target(PATH).request().put(Entity.entity("PUT", MediaType.TEXT_PLAIN));
+        assertEquals("PUT", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testDelete() {
+        Response response = target(PATH).request().delete();
+        assertEquals("DELETE", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java
new file mode 100644
index 0000000..5c0b7c3
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/AutoDiscoverableTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import java.io.IOException;
+
+import javax.ws.rs.ConstrainedTo;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.spi.AutoDiscoverable;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Note: Auto-discoverables from this test "affects" all other tests in suit.
+ *
+ * @author Michal Gajdos
+ */
+public class AutoDiscoverableTest extends JerseyTest {
+
+    private static final String PROPERTY = "AutoDiscoverableTest";
+
+    public static class CommonAutoDiscoverable implements AutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            // Return if PROPERTY is not true - applicable for other tests.
+            if (!PropertiesHelper.isProperty(context.getConfiguration().getProperty(PROPERTY))) {
+                return;
+            }
+
+            context.register(new WriterInterceptor() {
+                @Override
+                public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+                    context.setEntity(context.getEntity() + "-common");
+
+                    context.proceed();
+                }
+            }, 1);
+        }
+    }
+
+    @ConstrainedTo(RuntimeType.CLIENT)
+    public static class ClientAutoDiscoverable implements AutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            // Return if PROPERTY is not true - applicable for other tests.
+            if (!PropertiesHelper.isProperty(context.getConfiguration().getProperty(PROPERTY))) {
+                return;
+            }
+
+            context.register(new WriterInterceptor() {
+                @Override
+                public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+                    context.setEntity(context.getEntity() + "-client");
+
+                    context.proceed();
+                }
+            }, 10);
+        }
+    }
+
+    @ConstrainedTo(RuntimeType.SERVER)
+    public static class ServerAutoDiscoverable implements AutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            // Return if PROPERTY is not true - applicable for other tests.
+            if (!PropertiesHelper.isProperty(context.getConfiguration().getProperty(PROPERTY))) {
+                return;
+            }
+
+            context.register(new WriterInterceptor() {
+                @Override
+                public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+                    context.setEntity(context.getEntity() + "-server");
+
+                    context.proceed();
+                }
+            }, 10);
+        }
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        public String post(final String value) {
+            return value;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class).property(PROPERTY, true);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.property(PROPERTY, true);
+    }
+
+    @Test
+    public void testAutoDiscoverableConstrainedTo() throws Exception {
+        final Response response = target().request().post(Entity.text("value"));
+
+        assertEquals("value-common-client-common-server", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ConstrainedToTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ConstrainedToTest.java
new file mode 100644
index 0000000..79bbc5f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ConstrainedToTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.IOException;
+
+import javax.ws.rs.ConstrainedTo;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests whether providers are correctly validated in the client runtime (for example if provider constrained to
+ * server runtime is skipped in the client).
+
+ * @author Miroslav Fuksa
+ *
+ */
+public class ConstrainedToTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testClientWithProviderClasses() {
+        Client client = ClientBuilder.newClient(new ClientConfig(ClientFilterConstrainedToServer.class,
+                ClientFilterConstrainedToClient.class, ClientFilter.class));
+
+        _testFilters(client);
+    }
+
+    @Test
+    public void testClientWithProviderInstances() {
+        Client client = ClientBuilder.newClient(new ClientConfig(new ClientFilterConstrainedToServer(),
+                new ClientFilterConstrainedToClient(), new ClientFilter()));
+
+        _testFilters(client);
+    }
+
+    private void _testFilters(Client client) {
+        final Response response = client.target(getBaseUri()).path("resource").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("called", response.getHeaderString("ClientFilterConstrainedToClient"));
+        Assert.assertEquals("called", response.getHeaderString("ClientFilter"));
+        Assert.assertNull("The ClientFilterConstrainedToServer should not be called as it is constrained to server.",
+                response.getHeaderString("ClientFilterConstrainedToServer"));
+    }
+
+    @ConstrainedTo(RuntimeType.CLIENT)
+    public static class ClientFilterConstrainedToClient implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+            responseContext.getHeaders().add("ClientFilterConstrainedToClient", "called");
+        }
+    }
+
+    public static class ClientFilter implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+            responseContext.getHeaders().add("ClientFilter", "called");
+        }
+    }
+
+    /**
+     * Wrong configuration of Client filter which is constrained to server. This filter will be never called.
+     */
+    @ConstrainedTo(RuntimeType.SERVER)
+    public static class ClientFilterConstrainedToServer implements ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+            responseContext.getHeaders().add("ClientFilterConstrainedToServer", "called");
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+        @GET
+        public String get() {
+            return "get";
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ContentLengthTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ContentLengthTest.java
new file mode 100644
index 0000000..6e8785f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ContentLengthTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ContentLengthTest extends JerseyTest {
+
+    public static String STR = "string";
+
+    @Override
+    protected Application configure() {
+        ResourceConfig rc = new ResourceConfig();
+        rc.registerClasses(MyTypeResource.class, MyTypeWriter.class, ResourceGetByteNoHead.class);
+        return rc;
+    }
+
+    public static class MyType {
+
+        public String s = STR;
+    }
+
+    @Path("/")
+    public static class MyTypeResource {
+
+        @GET
+        public MyType getMyType() {
+            return new MyType();
+        }
+    }
+
+    @Provider
+    public static class MyTypeWriter implements MessageBodyWriter<MyType> {
+
+        @Override
+        public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
+            return aClass.equals(MyType.class);
+        }
+
+        @Override
+        public long getSize(MyType myType, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
+            return myType.s.length();
+        }
+
+        @Override
+        public void writeTo(MyType myType,
+                            Class<?> aClass,
+                            Type type,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> stringObjectMultivaluedMap,
+                            OutputStream outputStream) throws IOException, WebApplicationException {
+            outputStream.write(myType.s.getBytes());
+        }
+    }
+
+    @Test
+    public void testGetContentLengthCustomWriter() throws Exception {
+        Response response = target().request().get(Response.class);
+        assertEquals(200, response.getStatus());
+        assertEquals(STR.length(), Integer.parseInt(response.getHeaderString(HttpHeaders.CONTENT_LENGTH)));
+        assertTrue(response.hasEntity());
+    }
+
+    @Test
+    public void testHeadContentLengthCustomWriter() throws Exception {
+        Response response = target().request().head();
+        assertEquals(200, response.getStatus());
+        assertEquals(STR.length(), Integer.parseInt(response.getHeaderString(HttpHeaders.CONTENT_LENGTH)));
+        assertFalse(response.hasEntity());
+    }
+
+    @Path("/byte")
+    public static class ResourceGetByteNoHead {
+
+        @GET
+        public byte[] get() {
+            return "GET".getBytes();
+        }
+    }
+
+    @Test
+    public void testGetByte() throws Exception {
+        Response response = target().path("byte").request().get(Response.class);
+        assertEquals(200, response.getStatus());
+        assertEquals(3, Integer.parseInt(response.getHeaderString(HttpHeaders.CONTENT_LENGTH)));
+        assertTrue(response.hasEntity());
+    }
+
+    @Test
+    public void testHeadByte() throws Exception {
+        Response response = target().path("byte").request().head();
+        assertEquals(200, response.getStatus());
+        assertEquals(3, Integer.parseInt(response.getHeaderString(HttpHeaders.CONTENT_LENGTH)));
+        assertFalse(response.hasEntity());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ContentTypeTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ContentTypeTest.java
new file mode 100644
index 0000000..ad65f33
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ContentTypeTest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Type;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.logging.Logger;
+
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ContentTypeTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(ContentTypeTest.class.getName());
+
+    @Path(value = "/ContentType")
+    public static class ContentTypeResource {
+
+        @POST
+        @Produces("text/plain")
+        @SuppressWarnings("UnusedParameters")
+        public void postTest(final String str) {
+            // Ignore to generate response 204 - NoContent.
+        }
+
+        @POST
+        @Path("changeTest")
+        @Produces("foo/bar")
+        @CtFix(ContainerRequestFilter.class)
+        public String changeContentTypeTest(String echo) {
+            return echo;
+        }
+
+        @POST
+        @Path("null-content")
+        public String process(@HeaderParam(HttpHeaders.CONTENT_TYPE) String mediaType, @Context ContainerRequest request) {
+            return mediaType + "-" + request.hasEntity();
+        }
+
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    @interface CtFix {
+
+        Class<? super ContentTypeFixProvider> value();
+    }
+
+    public static class ContentTypeFixProvider
+            implements ReaderInterceptor, ContainerRequestFilter, ClientResponseFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+            System.out.println("filter1");
+            if (responseContext.getMediaType().toString().equals("foo/bar")) {
+                responseContext.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
+            }
+        }
+
+        @Override
+        public void filter(ContainerRequestContext context) throws IOException {
+            System.out.println("filter2");
+            if (context.getMediaType().toString().equals("foo/bar")) {
+                context.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
+            }
+        }
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            System.out.println("reader");
+            if (context.getMediaType().toString().equals("foo/bar")) {
+                context.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
+            }
+
+            return context.proceed();
+        }
+    }
+
+    public static class ContentTypeFixFeature implements DynamicFeature {
+
+        @Override
+        public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+            final CtFix annotation = resourceInfo.getResourceMethod().getAnnotation(CtFix.class);
+            if (annotation != null) {
+                context.register(ContentTypeFixProvider.class, annotation.value());
+            }
+        }
+    }
+
+    @Produces("foo/bar")
+    public static class FooBarStringWriter implements MessageBodyWriter<String> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == String.class && mediaType.toString().equals("foo/bar");
+        }
+
+        @Override
+        public long getSize(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return s.length();
+        }
+
+        @Override
+        public void writeTo(String s,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+
+            entityStream.write(s.getBytes());
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.DUMP_ENTITY);
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig()
+                .registerClasses(ContentTypeResource.class, ContentTypeFixFeature.class, FooBarStringWriter.class)
+                .registerInstances(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    @Test
+    public void testContentTypeHeaderForNoContentResponse() {
+        final Response response = target().path("ContentType").request().post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
+
+        assertEquals(204, response.getStatus());
+        assertNull(response.getHeaderString("Content-Type"));
+    }
+
+    @Test
+    public void testInvalidContentTypeHeader() throws Exception {
+        final URL url = new URL(getBaseUri().toString() + "ContentType");
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        connection.setRequestMethod("POST");
+        connection.setRequestProperty("Accept", "text/plain");
+        connection.setRequestProperty("Content-Type", "^^^");
+
+        connection.setDoOutput(true);
+        connection.connect();
+
+        final OutputStream outputStream = connection.getOutputStream();
+        outputStream.write("HelloWorld!".getBytes());
+        outputStream.write('\n');
+        outputStream.flush();
+
+        assertEquals(400, connection.getResponseCode());
+    }
+
+    @Test
+    public void testChangeContentTypeHeader() throws Exception {
+        WebTarget target;
+        Response response;
+
+        // filter test
+        target = target().path("ContentType").path("changeTest");
+        target.register(ContentTypeFixProvider.class, ClientResponseFilter.class)
+                .register(FooBarStringWriter.class);
+
+        response = target
+                .request().post(Entity.entity("test", "foo/bar"));
+
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+        assertEquals("test", response.readEntity(String.class));
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+
+        // interceptor test
+        target = target().path("ContentType").path("changeTest");
+        target.register(ContentTypeFixProvider.class, ReaderInterceptor.class)
+                .register(FooBarStringWriter.class);
+
+        response = target
+                .request().post(Entity.entity("test", "foo/bar"));
+
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.valueOf("foo/bar"), response.getMediaType());
+        assertEquals("test", response.readEntity(String.class));
+        assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
+    }
+
+    @Test
+    public void testEmptyPostRequestWithContentType() {
+        String response = target().path("ContentType/null-content").request("text/plain")
+                .post(Entity.entity(null, "foo/bar"), String.class);
+
+        assertEquals("foo/bar-false", response);
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/DetermineContentLengthTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/DetermineContentLengthTest.java
new file mode 100644
index 0000000..05cab0a
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/DetermineContentLengthTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test determining of the content length.
+ *
+ * @author Miroslav Fuksa
+ */
+public class DetermineContentLengthTest extends JerseyTest {
+
+    public static final int BUFFER_SIZE = 20;
+
+    public static final String STR0 = "";
+    public static final String STR6 = "123456";
+    public static final String STR12 = "123456789ABC";
+
+    @Path("root")
+    public static class Resource {
+
+        @GET
+        @Path("6")
+        @Produces("text/plain")
+        public String get6() {
+            return STR6;
+        }
+
+
+        @GET
+        @Path("0")
+        @Produces("text/plain")
+        public String get0() {
+            return STR0;
+        }
+
+
+        @GET
+        @Produces("text/plain")
+        @Path("12")
+        public String get5() {
+            return STR12;
+        }
+
+        @Path("test")
+        @POST
+        @Consumes("text/plain")
+        @Produces("text/plain")
+        public String testLength(String entity, @DefaultValue("-1") @HeaderParam(HttpHeaders.CONTENT_LENGTH) String length) {
+            return length;
+        }
+    }
+
+    @Priority(300)
+    public static class DoubleInterceptor implements WriterInterceptor, ReaderInterceptor {
+
+        @Override
+        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+            final OutputStream old = context.getOutputStream();
+            context.setOutputStream(new OutputStream() {
+
+                @Override
+                public void write(int b) throws IOException {
+                    old.write(b);
+                    old.write(b);
+                }
+            });
+            context.proceed();
+            context.setOutputStream(old);
+        }
+
+
+        @Override
+        public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+            final InputStream old = context.getInputStream();
+            try {
+                context.setInputStream(new InputStream() {
+
+                    @Override
+                    public int read() throws IOException {
+                        old.read();
+                        return old.read();
+                    }
+                });
+
+                return context.proceed();
+            } finally {
+                context.setInputStream(old);
+            }
+
+        }
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new InMemoryTestContainerFactory();
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(Resource.class, DoubleInterceptor.class);
+        resourceConfig.property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, BUFFER_SIZE);
+        return resourceConfig;
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, BUFFER_SIZE);
+        config.register(DoubleInterceptor.class);
+    }
+
+    @Test
+    public void test0() {
+        final Response response = target().path("root/0").request().get();
+        assertEquals(200, response.getStatus());
+        final Object headerContentLength = Integer.valueOf(response.getHeaderString(HttpHeaders.CONTENT_LENGTH));
+        assertEquals(STR0.length() * 2, headerContentLength);
+        assertEquals(STR0, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testHead0() {
+        final Response response = target().path("root/0").request().head();
+        assertEquals(200, response.getStatus());
+        final Object headerContentLength = Integer.valueOf(response.getHeaderString(HttpHeaders.CONTENT_LENGTH));
+        assertEquals(STR0.length() * 2, headerContentLength);
+    }
+
+    @Test
+    public void test6() {
+        final Response response = target().path("root/6").request().get();
+        assertEquals(200, response.getStatus());
+        final Object headerContentLength = Integer.valueOf(response.getHeaderString(HttpHeaders.CONTENT_LENGTH));
+        assertEquals(STR6.length() * 2, headerContentLength);
+        assertEquals(STR6, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testHead6() {
+        final Response response = target().path("root/6").request().head();
+        assertEquals(200, response.getStatus());
+        final Object headerContentLength = Integer.valueOf(response.getHeaderString(HttpHeaders.CONTENT_LENGTH));
+        assertEquals(STR6.length() * 2, headerContentLength);
+    }
+
+    @Test
+    public void test12() {
+        final Response response = target().path("root/12").request().get();
+        assertEquals(200, response.getStatus());
+        checkEmptyContentLength(response);
+        assertEquals(STR12, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testHead12() {
+        final Response response = target().path("root/12").request().head();
+        assertEquals(200, response.getStatus());
+        checkEmptyContentLength(response);
+    }
+
+    private void checkEmptyContentLength(Response response) {
+        final String headerString = response.getHeaderString(HttpHeaders.CONTENT_LENGTH);
+        if (headerString != null) {
+            final Object headerContentLength = Integer.valueOf(headerString);
+            assertEquals(-1, headerContentLength);
+        }
+    }
+
+    @Test
+    public void testClientLength0() {
+        final Response response = target().path("root/test").request().post(Entity.entity(STR0, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(STR0.length() * 2, response.readEntity(Integer.class).intValue());
+    }
+
+    @Test
+    public void testClientLength6() {
+        final Response response = target().path("root/test").request().post(Entity.entity(STR6, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(STR6.length() * 2, response.readEntity(Integer.class).intValue());
+    }
+
+    @Test
+    public void testClientLength12() {
+        final Response response = target().path("root/test").request().post(Entity.entity(STR12, MediaType.TEXT_PLAIN_TYPE));
+        assertEquals(200, response.getStatus());
+        assertEquals(-1, response.readEntity(Integer.class).intValue());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/EncodingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/EncodingTest.java
new file mode 100644
index 0000000..2e4ce8c
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/EncodingTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.filter.EncodingFilter;
+import org.glassfish.jersey.message.DeflateEncoder;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class EncodingTest extends JerseyTest {
+    @Path("/")
+    public static class EchoResource {
+        @POST
+        public String post(String text) {
+            return text;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(
+                EchoResource.class,
+                org.glassfish.jersey.server.filter.EncodingFilter.class,
+                GZipEncoder.class,
+                DeflateEncoder.class
+        );
+    }
+
+    @Test
+    public void testGZip() throws IOException {
+        test(new TestSpec() {
+            @Override
+            public InputStream decode(InputStream stream) throws IOException {
+                return new GZIPInputStream(stream);
+            }
+
+            @Override
+            public void checkHeadersAndStatus(Response response) {
+                assertEquals("gzip", response.getHeaderString(HttpHeaders.CONTENT_ENCODING));
+            }
+
+            @Override
+            public Invocation.Builder setHeaders(Invocation.Builder invBuilder) {
+                return invBuilder.header(HttpHeaders.ACCEPT_ENCODING, "gzip");
+            }
+        });
+    }
+
+    @Test
+    public void testDeflate() throws IOException {
+        test(new TestSpec() {
+            @Override
+            public InputStream decode(InputStream stream) throws IOException {
+                return new InflaterInputStream(stream);
+            }
+
+            @Override
+            public void checkHeadersAndStatus(Response response) {
+                assertEquals("deflate", response.getHeaderString(HttpHeaders.CONTENT_ENCODING));
+            }
+
+            @Override
+            public Invocation.Builder setHeaders(Invocation.Builder invBuilder) {
+                return invBuilder.header(HttpHeaders.ACCEPT_ENCODING, "deflate");
+            }
+        });
+    }
+
+    @Test
+    public void testGZipPreferred() throws IOException {
+        test(new TestSpec() {
+            @Override
+            public InputStream decode(InputStream stream) throws IOException {
+                return new GZIPInputStream(stream);
+            }
+
+            @Override
+            public void checkHeadersAndStatus(Response response) {
+                assertEquals("x-gzip", response.getHeaderString(HttpHeaders.CONTENT_ENCODING));
+            }
+
+            @Override
+            public Invocation.Builder setHeaders(Invocation.Builder invBuilder) {
+                return invBuilder.header(HttpHeaders.ACCEPT_ENCODING, "deflate; q=.5, x-gzip");
+            }
+        });
+    }
+
+    @Test
+    public void testGZipClientServer() throws IOException {
+        test(new TestSpec() {
+            @Override
+            public WebTarget configure(WebTarget target) {
+                target.register(GZipEncoder.class).register(EncodingFilter.class);
+                return target;
+            }
+
+            @Override
+            public void checkHeadersAndStatus(Response response) {
+                assertEquals("gzip", response.getHeaderString(HttpHeaders.CONTENT_ENCODING));
+            }
+        });
+    }
+
+    @Test
+    public void testDeflatePreferredClientServer() throws IOException {
+        test(new TestSpec() {
+            @Override
+            public Invocation.Builder setHeaders(Invocation.Builder invBuilder) {
+                return invBuilder.header(HttpHeaders.ACCEPT_ENCODING, "deflate,gzip=.5");
+            }
+
+            @Override
+            public WebTarget configure(WebTarget target) {
+                target.register(DeflateEncoder.class)
+                        .register(GZipEncoder.class).register(EncodingFilter.class);
+                return target;
+            }
+
+            @Override
+            public void checkHeadersAndStatus(Response response) {
+                assertEquals("deflate", response.getHeaderString(HttpHeaders.CONTENT_ENCODING));
+            }
+        });
+    }
+
+    private void test(TestSpec testSpec) throws IOException {
+        String input = "hello";
+        Response response = testSpec.setHeaders(testSpec.configure(target()).request())
+                .post(Entity.text(input));
+        byte[] output = response.readEntity(byte[].class);
+        testSpec.checkHeadersAndStatus(response);
+        InputStream decoded = testSpec.decode(new ByteArrayInputStream(output));
+        byte[] result = new byte[input.getBytes().length];
+        int len = decoded.read(result);
+        assertEquals(-1, decoded.read());
+        decoded.close();
+        assertEquals(input.getBytes().length, len);
+        assertArrayEquals(input.getBytes(), result);
+    }
+
+    private static class TestSpec {
+        public InputStream decode(InputStream stream) throws IOException {
+            return stream;
+        }
+
+        public Invocation.Builder setHeaders(Invocation.Builder invBuilder) {
+            return invBuilder;
+        }
+
+        public WebTarget configure(WebTarget target) {
+            return target;
+        }
+
+        public void checkHeadersAndStatus(Response response) {
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ForcedAutoDiscoverableTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ForcedAutoDiscoverableTest.java
new file mode 100644
index 0000000..7defa2d
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ForcedAutoDiscoverableTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014, 2018 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.common;
+
+import java.io.IOException;
+
+import javax.ws.rs.ConstrainedTo;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.RuntimeType;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Note: Auto-discoverables from this test "affects" all other tests in suit.
+ *
+ * @author Michal Gajdos
+ */
+public class ForcedAutoDiscoverableTest extends JerseyTest {
+
+    private static final String PROPERTY = "ForcedAutoDiscoverableTest";
+
+    public static class CommonAutoDiscoverable implements ForcedAutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            // Return if PROPERTY is not true - applicable for other tests.
+            if (!PropertiesHelper.isProperty(context.getConfiguration().getProperty(PROPERTY))) {
+                return;
+            }
+
+            context.register(new WriterInterceptor() {
+                @Override
+                public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+                    context.setEntity(context.getEntity() + "-common");
+
+                    context.proceed();
+                }
+            }, 1);
+        }
+    }
+
+    @ConstrainedTo(RuntimeType.CLIENT)
+    public static class ClientAutoDiscoverable implements ForcedAutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            // Return if PROPERTY is not true - applicable for other tests.
+            if (!PropertiesHelper.isProperty(context.getConfiguration().getProperty(PROPERTY))) {
+                return;
+            }
+
+            context.register(new WriterInterceptor() {
+                @Override
+                public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+                    context.setEntity(context.getEntity() + "-client");
+
+                    context.proceed();
+                }
+            }, 10);
+        }
+    }
+
+    @ConstrainedTo(RuntimeType.SERVER)
+    public static class ServerAutoDiscoverable implements ForcedAutoDiscoverable {
+
+        @Override
+        public void configure(final FeatureContext context) {
+            // Return if PROPERTY is not true - applicable for other tests.
+            if (!PropertiesHelper.isProperty(context.getConfiguration().getProperty(PROPERTY))) {
+                return;
+            }
+
+            context.register(new WriterInterceptor() {
+                @Override
+                public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+                    context.setEntity(context.getEntity() + "-server");
+
+                    context.proceed();
+                }
+            }, 10);
+        }
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        public String post(final String value) {
+            return value;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class)
+                .property(PROPERTY, true)
+                .property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE_SERVER, true)
+                .property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_SERVER, true);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.property(PROPERTY, true)
+        .property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT, true)
+        .property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE_CLIENT, true);
+    }
+
+    @Test
+    public void testForcedAutoDiscoverable() throws Exception {
+        final Response response = target().request().post(Entity.text("value"));
+
+        assertThat(response.readEntity(String.class), is("value-common-client-common-server"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/HeaderParamTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/HeaderParamTest.java
new file mode 100644
index 0000000..0a328f4
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/HeaderParamTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010, 2018 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.common;
+
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Miroslav Fuksa
+ */
+public class HeaderParamTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MyResource.class, LoggingFeature.class);
+    }
+
+    @Path("resource")
+    public static class MyResource {
+
+        @GET
+        public String get(@HeaderParam("hello") List<String> headers) {
+            return headers.size() + ":" + headers;
+        }
+    }
+
+    @Test
+    public void testHeaderListSingleHeader() throws Exception {
+        Response response = target().path("resource").request().header("hello", "world").header("hello", "universe").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("1:[world,universe]"));
+    }
+
+    /**
+     * Check that multi value http headers are correctly read by the server.
+     */
+    @Test
+    public void testHeaderListMultipleHeaders() throws Exception {
+        final URL url = new URL(getBaseUri().toString() + "resource");
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        connection.setRequestMethod("GET");
+        connection.setRequestProperty("Accept", "text/plain");
+        connection.setRequestProperty("hello", "world");
+        connection.addRequestProperty("hello", "universe");
+
+        connection.setDoOutput(false);
+        connection.connect();
+
+        assertThat(connection.getResponseCode(), equalTo(200));
+        assertThat(ReaderWriter.readFromAsString(new InputStreamReader(connection.getInputStream())),
+                equalTo("2:[world, universe]"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/HeaderValueExceptionTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/HeaderValueExceptionTest.java
new file mode 100644
index 0000000..97f5305
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/HeaderValueExceptionTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests HeaderValueException.
+ *
+ * @author Miroslav Fuksa
+ */
+public class HeaderValueExceptionTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ResponseFilter.class, TestResource.class);
+    }
+
+    @Test
+    public void testInboundHeaderThrowsException() throws ExecutionException, InterruptedException {
+        final Response response = target("resource/inbound").request()
+                .header(HttpHeaders.DATE, "foo")
+                .post(Entity.entity("inbound", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testOutboundHeaderThrowsException() throws ExecutionException, InterruptedException {
+        final Response response = target("resource/outbound").request()
+                .post(Entity.entity("outbound", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testOutboundResponseHeaderThrowsException() throws ExecutionException, InterruptedException {
+        final Response response = target("resource/outbound-Response").request()
+                .post(Entity.entity("outbound", MediaType.TEXT_PLAIN_TYPE));
+        Assert.assertEquals(500, response.getStatus());
+    }
+
+
+    public static class ResponseFilter implements ContainerResponseFilter {
+
+        @Override
+        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+            // this call should throw HeaderValueException which will be converted to HTTP 500 response
+            responseContext.getDate();
+        }
+    }
+
+    @Path("resource")
+    public static class TestResource {
+        @POST
+        @Path("inbound")
+        public String postInbound(String entity, @Context HttpHeaders headers) {
+            // this call should throw HeaderValueException which will be converted to HTTP 400 response
+            headers.getDate();
+            return entity;
+        }
+
+        @POST
+        @Path("outbound")
+        public Response postOutbound(String entity) {
+            return Response.ok().entity(entity).header(HttpHeaders.DATE, "bar").build();
+        }
+
+        @POST
+        @Path("outbound-Response")
+        public Response postOutboundResponse(String entity) {
+            final Response response = Response.ok(entity).header(HttpHeaders.DATE, "foo").build();
+            // this call should throw HeaderValueException which will be converted to HTTP 500 response
+            response.getDate();
+            return response;
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/Hk2BinderSupportTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/Hk2BinderSupportTest.java
new file mode 100644
index 0000000..7ecb616
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/Hk2BinderSupportTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017, 2018 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.common;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that HK2Binder and Jersey Binder work together.
+ *
+ * @author Petr Bouda
+ */
+public class Hk2BinderSupportTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        ResourceConfig config = new ResourceConfig();
+        config.register(HelloWorldResource.class);
+        config.register(new Hk2Binder());
+        config.register(new JerseyBinder());
+        return config;
+    }
+
+    @Test
+    public void testResponse() {
+        String s = target().path("helloworld").request().get(String.class);
+        assertEquals(Hk2Binder.HK2_HELLO_MESSAGE + "/" + JerseyBinder.JERSEY_HELLO_MESSAGE, s);
+    }
+
+    private static class Hk2Binder extends AbstractBinder {
+        private static final String HK2_HELLO_MESSAGE = "Hello HK2!";
+
+        @Override
+        protected void configure() {
+            bind(HK2_HELLO_MESSAGE).to(String.class).named("hk2");
+        }
+    }
+
+    private static class JerseyBinder extends org.glassfish.jersey.internal.inject.AbstractBinder {
+        private static final String JERSEY_HELLO_MESSAGE = "Hello Jersey!";
+
+        @Override
+        protected void configure() {
+            bind(JERSEY_HELLO_MESSAGE).to(String.class).named("jersey");
+        }
+    }
+
+    @Path("helloworld")
+    public static class HelloWorldResource {
+
+        @Inject
+        @Named("hk2")
+        private String hk2Hello;
+
+        @Inject
+        @Named("jersey")
+        private String jerseyHello;
+
+        @GET
+        public String getHello() {
+            return hk2Hello + "/" + jerseyHello;
+        }
+    }
+}
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
new file mode 100644
index 0000000..f15e5d8
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.lessThan;
+
+/**
+ * {@link LoggingFeature} end-to-end tests.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        LoggingFeatureTest.ClientTest.class,
+        LoggingFeatureTest.ContainerTest.class,
+        LoggingFeatureTest.ContainerAutodiscoveryTest.class,
+        LoggingFeatureTest.FiltersOrderTest.class
+})
+public class LoggingFeatureTest {
+
+    private static final String LOGGER_NAME = "org.glassfish.jersey.logging.feature";
+    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";
+
+    @Path("/")
+    public static class MyResource {
+
+        @GET
+        @Produces(BINARY_MEDIA_TYPE)
+        public Response getHeadersAndBinaryPayload() {
+            return Response
+                    .ok(ENTITY)
+                    .header("001", "First Header Value")
+                    .header("002", "Second Header Value")
+                    .header("003", "Third Header Value")
+                    .header("004", "Fourth Header Value")
+                    .header("005", "Fifth Header Value")
+                    .build();
+        }
+
+        @Path("/text")
+        @GET
+        @Produces(TEXT_MEDIA_TYPE)
+        public Response getHeadersAndTextPayload() {
+            return Response
+                    .ok(ENTITY)
+                    .header("001", "First Header Value")
+                    .header("002", "Second Header Value")
+                    .header("003", "Third Header Value")
+                    .header("004", "Fourth Header Value")
+                    .header("005", "Fifth Header Value")
+                    .build();
+        }
+
+        @Path("/text")
+        @POST
+        @Produces(TEXT_MEDIA_TYPE)
+        public Response post(String text) {
+            return Response
+                    .ok(ENTITY)
+                    .build();
+        }
+
+    }
+
+    /**
+     * General client side tests.
+     */
+    public static class ClientTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
+
+            return new ResourceConfig(MyResource.class);
+        }
+
+        @Test
+        public void testFilterAsClientRequestFilter() throws Exception {
+            final Response response = target()
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME)))
+                    .request()
+                    .get();
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+        }
+
+        @Test
+        public void testOrderOfHeadersOnClient() throws Exception {
+            final Response response = target()
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME)))
+                    .request()
+                    .get();
+            assertThat(response.readEntity(String.class), equalTo(ENTITY));
+
+            final LogRecord record = getLoggingFilterResponseLogRecord(getLoggedRecords());
+            final String message = record.getMessage();
+
+            int i = 1;
+            do {
+                final String h1 = "00" + i++;
+                final String h2 = "00" + i;
+
+                final int i1 = message.indexOf(h1);
+                final int i2 = message.indexOf(h2);
+
+                assertThat("Header " + h1 + " has been logged sooner than header " + h2, i1, lessThan(i2));
+            } while (i < 5);
+        }
+
+        @Test
+        public void testVerbosityAnyPayload() throws Exception {
+            final Response response = target()
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_ANY))
+                    .request()
+                    .get();
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
+        }
+
+        @Test
+        public void testVerbosityAnyPayloadSetVerbosityAsText() throws Exception {
+            final Response response = target()
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME)))
+                    .property(LoggingFeature.LOGGING_FEATURE_VERBOSITY, "PAYLOAD_ANY")
+                    .request()
+                    .get();
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
+        }
+
+        @Test
+        public void testVerbosityTextPayloadBinaryFiltered() throws Exception {
+            final Response response = target()
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_TEXT))
+                    .request()
+                    .get();
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), not(containsString(ENTITY)));
+        }
+
+        @Test
+        public void testVerbosityTextPayload() throws Exception {
+            final Response response = target("/text")
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_TEXT))
+                    .request()
+                    .get();
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
+        }
+
+        @Test
+        public void testVerbosityHeadersPayload() throws Exception {
+            final Response response = target()
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.HEADERS_ONLY))
+                    .request()
+                    .get();
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), not(containsString(ENTITY)));
+        }
+
+        @Test
+        public void testPostedEntityLogged() throws Exception {
+            final Response response = target("/text")
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_TEXT))
+                    .request()
+                    .post(Entity.text(ENTITY));
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(0).getMessage(), containsString(ENTITY));
+        }
+
+    }
+
+    /**
+     * General client side tests.
+     */
+    public static class ContainerTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
+
+            return new ResourceConfig(MyResource.class)
+                    .register(LoggingFeature.class)
+                    .property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER, LOGGER_NAME);
+        }
+
+        @Test
+        public void testLoggingAsContainer() throws Exception {
+            // Correct response status.
+            assertThat(target().request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
+
+            // Check logs for proper id.
+            assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+            assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+        }
+
+        @Test
+        public void testLoggingAsContainerTextPayload() throws Exception {
+            // Correct response status.
+            assertThat(target("/text").request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
+
+            // Check logs for proper id.
+            assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+            assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
+        }
+
+        @Test
+        public void testLoggingAsContainerBinaryPayload() throws Exception {
+            // Correct response status.
+            assertThat(target().request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
+
+            // Check logs for proper id.
+            assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+            assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), not(containsString(ENTITY)));
+        }
+
+        @Test
+        public void testPostedEntityLogged() throws Exception {
+            final Response response = target("/text")
+                    .request()
+                    .post(Entity.text(ENTITY));
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper id.
+            assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(0).getMessage(), containsString(ENTITY));
+
+        }
+    }
+
+    /**
+     * General client side tests.
+     */
+    public static class ContainerAutodiscoveryTest extends JerseyTest {
+
+        @Override
+        protected Application configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.INFO.intValue());
+
+            return new ResourceConfig(MyResource.class)
+                    .property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER, LOGGER_NAME)
+                    .property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, "INFO");
+        }
+
+        @Test
+        public void testFilterAsContainerFilter() throws Exception {
+            // Correct response status.
+            assertThat(target().request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
+
+            // Check logs for proper id.
+            assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+            assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
+        }
+    }
+
+    private static LogRecord getLoggingFilterRequestLogRecord(final List<LogRecord> records) {
+        return getLoggingFilterLogRecord(records, true);
+    }
+
+    private static LogRecord getLoggingFilterResponseLogRecord(final List<LogRecord> records) {
+        return getLoggingFilterLogRecord(records, false);
+    }
+
+    private static LogRecord getLoggingFilterLogRecord(final List<LogRecord> records, final boolean requestQuery) {
+        for (final LogRecord record : getLoggingFilterLogRecord(records)) {
+            if (record.getMessage().contains(requestQuery ? "request" : "response")) {
+                return record;
+            }
+        }
+
+        throw new AssertionError("Unable to find proper log record.");
+    }
+
+    private static List<LogRecord> getLoggingFilterLogRecord(final List<LogRecord> records) {
+        final List<LogRecord> loggingFilterRecords = new ArrayList<>(records.size());
+
+        for (final LogRecord record : records) {
+            if (record.getLoggerName().startsWith(LOGGER_NAME)) {
+                loggingFilterRecords.add(record);
+            }
+        }
+
+        return loggingFilterRecords;
+    }
+
+    public static class FiltersOrderTest extends JerseyTest {
+
+        @Priority(1000)
+        private static class CustomFilter implements ClientRequestFilter, ClientResponseFilter,
+                                                     ContainerRequestFilter, ContainerResponseFilter {
+
+            static final String CUSTOM_HEADER = "custom_header";
+
+            @Override
+            public void filter(final ClientRequestContext requestContext) throws IOException {
+                requestContext.getHeaders().add(CUSTOM_HEADER, "client/request");
+            }
+
+            @Override
+            public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext)
+                    throws IOException {
+                responseContext.getHeaders().add(CUSTOM_HEADER, "client/response");
+            }
+
+            @Override
+            public void filter(final ContainerRequestContext requestContext) throws IOException {
+                requestContext.getHeaders().add(CUSTOM_HEADER, "container/request");
+            }
+
+            @Override
+            public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
+                    throws IOException {
+                responseContext.getHeaders().add(CUSTOM_HEADER, "container/response");
+            }
+        }
+
+        @Override
+        protected Application configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.INFO.intValue());
+
+            return new ResourceConfig(MyResource.class)
+                    .property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME, LOGGER_NAME)
+                    .property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL, "INFO")
+                    .register(CustomFilter.class);
+        }
+
+        @Test
+        public void testFilterAsContainerFilter() throws Exception {
+            // Correct response status.
+            assertThat(target()
+                    .register(CustomFilter.class)
+                    .register(new LoggingFeature(Logger.getLogger(LOGGER_NAME),
+                            Level.INFO,
+                            LoggingFeature.Verbosity.HEADERS_ONLY,
+                            0))
+                    .request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
+
+            for (LogRecord record : getLoggedRecords()) {
+                System.out.println(record.getMessage());
+            }
+
+            // --- client request log entry
+            // client added header before request has sent (and logged)
+            assertThat(getLoggedRecords().get(0).getMessage(),
+                    containsString("1 > custom_header: client/request\n"));
+
+
+            // --- container request log entry
+            // container receives header from client request
+            assertThat(getLoggedRecords().get(1).getMessage(),
+                    containsString("1 > custom_header: client/request\n"));
+            // container has added its own header after logging filter logged message
+            assertThat(getLoggedRecords().get(1).getMessage(),
+                    not(containsString("1 > custom_header: container/request\n")));
+
+
+            // --- container response log entry
+            // container added header to the response and it was logged
+            assertThat(getLoggedRecords().get(2).getMessage(),
+                    containsString("1 < custom_header: container/response\n"));
+
+            // --- client response log entry
+            // client received header
+            assertThat(getLoggedRecords().get(3).getMessage(),
+                    containsString("1 < custom_header: container/response\n"));
+            assertThat(getLoggedRecords().get(3).getMessage(),
+                    not(containsString("1 < custom_header: client/response\n")));
+
+        }
+
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyReaderUnsupportedTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyReaderUnsupportedTest.java
new file mode 100644
index 0000000..cdd4965
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyReaderUnsupportedTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test case for unsupported media type.
+ *
+ * @author Miroslav Fuksa
+ */
+public class MessageBodyReaderUnsupportedTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // TestEntityProvider must not be registered in the application for this test case.
+        return new ResourceConfig(Resource.class);
+    }
+
+    /**
+     * Send request to with application/json content to server where JsonJaxbBinder is not registered. UNSUPPORTED_MEDIA_TYPE
+     * should be returned.
+     */
+    @Test
+    public void testUnsupportedMessageBodyReader() {
+        client().register(new TestEntityProvider());
+        TestEntity entity = new TestEntity("testEntity");
+        Response response = target().path("test").request(TestEntityProvider.TEST_ENTITY_TYPE)
+                .post(Entity.entity(entity, TestEntityProvider.TEST_ENTITY_TYPE));
+
+        // TestEntityProvider is not registered on the server and therefore the server should return UNSUPPORTED_MEDIA_TYPE
+        assertEquals(Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode(), response.getStatus());
+        assertFalse(Resource.methodCalled);
+        String responseEntity = response.readEntity(String.class);
+        assertTrue((responseEntity == null) || (responseEntity.length() == 0));
+    }
+
+    /**
+     * Test Resource class.
+     *
+     * @author Miroslav Fuksa
+     */
+    @Path("test")
+    public static class Resource {
+
+        private static volatile boolean methodCalled;
+
+        /**
+         * Resource method producing a {@code null} result.
+         *
+         * @param entity test entity.
+         * @return {@code null}.
+         */
+        @POST
+        @Produces(TestEntityProvider.TEST_ENTITY)
+        @Consumes(TestEntityProvider.TEST_ENTITY)
+        @SuppressWarnings("UnusedParameters")
+        public TestEntity processEntityAndProduceNull(TestEntity entity) {
+            methodCalled = true;
+            return null;
+        }
+    }
+
+    /**
+     * Test bean.
+     *
+     * @author Miroslav Fuksa
+     */
+    public static class TestEntity {
+
+        private final String value;
+
+        /**
+         * Get value.
+         *
+         * @return value.
+         */
+        public String getValue() {
+            return value;
+        }
+
+        /**
+         * Create new test entity.
+         *
+         * @param value entity value.
+         */
+        public TestEntity(String value) {
+            super();
+            this.value = value;
+        }
+    }
+
+    /**
+     * Custom test entity provider.
+     */
+    @Produces("test/entity")
+    @Consumes("test/entity")
+    public static class TestEntityProvider implements MessageBodyReader<TestEntity>, MessageBodyWriter<TestEntity> {
+        /**
+         * Test bean media type string.
+         */
+        public static final String TEST_ENTITY = "test/entity";
+        /**
+         * Test bean media type.
+         */
+        public static final MediaType TEST_ENTITY_TYPE = MediaType.valueOf(TEST_ENTITY);
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return TestEntity.class == type && TEST_ENTITY_TYPE.equals(mediaType);
+        }
+
+        @Override
+        public TestEntity readFrom(Class<TestEntity> type,
+                                   Type genericType,
+                                   Annotation[] annotations,
+                                   MediaType mediaType,
+                                   MultivaluedMap<String, String> httpHeaders,
+                                   InputStream entityStream) throws IOException, WebApplicationException {
+            return new TestEntity(ReaderWriter.readFromAsString(entityStream, mediaType));
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return TestEntity.class == type && TEST_ENTITY_TYPE.equals(mediaType);
+        }
+
+        @Override
+        public long getSize(TestEntity testEntity,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(TestEntity testEntity,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            ReaderWriter.writeToAsString(testEntity.getValue(), entityStream, mediaType);
+        }
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyWorkerInheritanceTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyWorkerInheritanceTest.java
new file mode 100644
index 0000000..81d2fa9
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyWorkerInheritanceTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test-case for JERSEY-1481.
+ *
+ * {@link JacksonFeature Jackson provider} should not take precedence over our
+ * custom provider.
+ *
+ * @author Michal Gajdos
+ */
+public class MessageBodyWorkerInheritanceTest extends JerseyTest {
+
+    public static interface Model<T> {
+
+        public T getValue();
+    }
+
+    public static class StringModel implements Model<String> {
+
+        private final String value;
+
+        public StringModel(final String value) {
+            this.value = value;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+    }
+
+    public static interface InterfaceType extends Model {
+    }
+
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Provider
+    public static class ModelReader<T extends Model> implements MessageBodyReader<T> {
+
+        @Override
+        public boolean isReadable(
+                        Class<?> type,
+                        Type genericType,
+                        Annotation[] annotations,
+                        MediaType mediaType) {
+
+            return Model.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public T readFrom(
+                    Class<T> type,
+                    Type genericType,
+                    Annotation[] annotations,
+                    MediaType mediaType,
+                    MultivaluedMap<String, String> httpHeaders,
+                    InputStream entityStream) throws IOException, WebApplicationException {
+
+            return (T) new InterfaceType() {
+                @Override
+                public Object getValue() {
+                    return "fromInterfaceTypeReader";
+                }
+            };
+        }
+    }
+
+    @Provider
+    public abstract static class BaseProvider<T> implements MessageBodyWriter<T> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(final T t,
+                final Class<?> type,
+                final Type genericType,
+                final Annotation[] annotations,
+                final MediaType mediaType) {
+            return -1;
+        }
+
+        @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 {
+            entityStream.write(getContent(t).getBytes("UTF-8"));
+        }
+
+        public abstract String getContent(T proxy);
+    }
+
+    @Provider
+    @Produces(MediaType.APPLICATION_JSON)
+    public static class GenericModelWriter extends BaseProvider<Model> {
+
+        @Override
+        public String getContent(final Model proxy) {
+            return "{\"bar\":\"" + proxy.getValue() + "\"}";
+        }
+    }
+
+    @Provider
+    @Produces(MediaType.APPLICATION_JSON)
+    public static class IntegerModelWriter extends BaseProvider<Model<Integer>> {
+
+        @Override
+        public String getContent(final Model<Integer> proxy) {
+            return "{\"foo\":\"" + proxy.getValue() + "\"}";
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        public Model<String> getStringModel() {
+            return new StringModel("foo");
+        }
+
+        @POST
+        @Produces(MediaType.TEXT_PLAIN)
+        public String post(InterfaceType t) {
+            return t.getValue().toString();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class)
+                .register(GenericModelWriter.class)
+                .register(ModelReader.class)
+                .register(JacksonFeature.class);
+    }
+
+    @Test
+    public void testMessageBodyWorkerInheritance() throws Exception {
+        assertEquals("{\"bar\":\"foo\"}", target().path("resource").request(MediaType.APPLICATION_JSON_TYPE).get(String.class));
+    }
+
+    @Test
+    public void testMessageBodyWorkerInterfaceInheritance() throws Exception {
+
+        final Response response = target().path("resource")
+                                     .request().post(Entity.json("{\"value\":\"ignored\"}"));
+
+        assertEquals(200, response.getStatus());
+        assertEquals("fromInterfaceTypeReader", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyWriterObjectDistanceTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyWriterObjectDistanceTest.java
new file mode 100644
index 0000000..4a792e0
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/MessageBodyWriterObjectDistanceTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2017, 2018 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.common;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+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 org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Object.class needs special handling when computing type distance - it should be always further than any other
+ * implemented interface.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@SuppressWarnings("WeakerAccess")
+public class MessageBodyWriterObjectDistanceTest extends JerseyTest {
+
+    public interface InterfaceA {
+
+    }
+
+    public interface InterfaceB extends InterfaceA {
+
+    }
+
+    @Provider
+    @Produces("application/test")
+    public static class ObjectWriter implements MessageBodyWriter<Object> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return true;
+        }
+
+        @Override
+        public long getSize(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                WebApplicationException {
+            entityStream.write("object".getBytes("UTF-8"));
+        }
+    }
+
+    @Provider
+    @Produces("application/test")
+    public static class InterfaceAWriter implements MessageBodyWriter<InterfaceA> {
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return InterfaceA.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(InterfaceA interfaceA, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(InterfaceA interfaceA, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            entityStream.write(InterfaceA.class.getSimpleName().getBytes("UTF-8"));
+        }
+    }
+
+    @Path("resource")
+    public static class Resource {
+
+        @GET
+        @Produces("application/test")
+        public InterfaceB getStringModel() {
+            return new InterfaceB() {
+            };
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class)
+                .register(ObjectWriter.class)
+                .register(InterfaceAWriter.class);
+    }
+
+    @Test
+    public void testMessageBodyWriterObjectDistance() throws Exception {
+        assertEquals(InterfaceA.class.getSimpleName(),
+                     target().path("resource").request("application/test").get(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/NoEntityTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/NoEntityTest.java
new file mode 100644
index 0000000..201f846
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/NoEntityTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests that no content type is sent when an entity is not present.
+ *
+ * @author Miroslav Fuksa
+ */
+public class NoEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(MyResource.class, MoxyJsonFeature.class);
+        return resourceConfig;
+    }
+
+    @XmlRootElement
+    public static class MyEntity {
+
+        @XmlAttribute
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    @Path("resource")
+    public static class MyResource {
+
+        @GET
+        @Produces("application/json")
+        @Path("no-entity")
+        public Response getNoEntity() {
+            return Response.status(204).build();
+        }
+
+        @GET
+        @Produces("application/json")
+        @Path("entity")
+        public Response getEntity() {
+            MyEntity myEntity = new MyEntity();
+            myEntity.setName("hello");
+            return Response.status(200).entity(myEntity).build();
+        }
+
+        @GET
+        @Produces("text/plain")
+        @Path("string")
+        public Response getEmptyString() {
+
+            return Response.status(204).entity("").build();
+        }
+
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(
+                new LoggingFeature(Logger.getLogger(NoEntityTest.class.toString()), LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    /**
+     * Tests that returned media type is null when no entity is sent.
+     */
+    @Test
+    public void testNoEntity() {
+        Response response = target().path("resource/no-entity").request(MediaType.APPLICATION_JSON_TYPE).get();
+        MyEntity myEntity = response.readEntity(MyEntity.class);
+        assertNull(myEntity);
+        assertEquals(204, response.getStatus());
+        assertNull(response.getMediaType());
+    }
+
+    /**
+     * Tests that correct media type is returned.
+     */
+    @Test
+    public void testEntity() {
+        Response response = target().path("resource/entity").request(MediaType.APPLICATION_JSON_TYPE).get();
+        MyEntity myEntity = response.readEntity(MyEntity.class);
+        assertEquals("hello", myEntity.getName());
+        assertEquals(200, response.getStatus());
+        assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType());
+    }
+
+    /**
+     * Tests that entity is read as null when no entity is sent with 204 response status.
+     * Currently this test throws an exception when trying to read the entity.
+     * <p/>
+     * Exception:
+     * <p/>
+     * org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException:
+     * MessageBodyReader not found for media type=text/plain,
+     * type=class org.glassfish.jersey.tests.e2e.common.NoEntityTest$MyEntity,
+     * genericType=class org.glassfish.jersey.tests.e2e.common.NoEntityTest$MyEntity.
+     * <p/>
+     * https://java.net/jira/browse/JERSEY-1994
+     */
+    @Test
+    @Ignore("see https://java.net/jira/browse/JERSEY-1994")
+    public void testNoEntityString() {
+        Response response = target().path("resource/string").request().get();
+        MyEntity myEntity = response.readEntity(MyEntity.class);
+        assertNull(myEntity);
+        assertEquals(204, response.getStatus());
+        assertEquals("text/plain", response.getMediaType());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new SimpleTestContainerFactory();
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/NonPublicNonStaticTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/NonPublicNonStaticTest.java
new file mode 100644
index 0000000..f28905c
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/NonPublicNonStaticTest.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2014, 2018 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.common;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+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 org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests deployment of non-standard or invalid resources and providers.
+ * <p/>
+ * The tests are checking, that the failure (if expected) takes place in the expected time (during deployment,
+ * or on first request).
+ *
+ * @author Paul Sandoz
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({NonPublicNonStaticTest.NonStaticResourceTest.class,
+        NonPublicNonStaticTest.NonStaticResourceSubResourceTest.class,
+        NonPublicNonStaticTest.NonPublicResourceTest.class,
+        NonPublicNonStaticTest.NonPublicResourceSubResourceTest.class,
+        NonPublicNonStaticTest.AbstractResourceTest.class,
+        NonPublicNonStaticTest.AbstractResourceSubResourceTest.class,
+        NonPublicNonStaticTest.InterfaceResourceTest.class,
+        NonPublicNonStaticTest.InterfaceResourceSubResourceTest.class,
+        NonPublicNonStaticTest.NonPublicResourceWithConstructorTest.class,
+        NonPublicNonStaticTest.NonPublicResourceSubresourceWithConstructorTest.class,
+        NonPublicNonStaticTest.PublicResourceWithPrivateConstructorTest.class,
+        NonPublicNonStaticTest.NonStaticProviderTest.class,
+        NonPublicNonStaticTest.NonPublicProviderTest.class,
+        NonPublicNonStaticTest.PublicProviderWithPrivateConstructorTest.class})
+public class NonPublicNonStaticTest {
+
+    // various resources and providers for the test bellow
+    // as we need to create and deploy separate and customized resourceConfigs for each test case,
+    // the testcases are encapsulated in inner-classes, which are subclasses of JerseyTest.
+    // Those resources and providers are, however in the outter class (can be reused inside resource configs)
+
+    @Path("/non-static")
+    public class NonStaticResource {
+
+        public String get() {
+            return "Hi";
+        }
+    }
+
+    @Path("/non-static-sub")
+    public static class NonStaticResourceSubResource {
+
+        @Path("class")
+        public Class<NonStaticResource> getClazz() {
+            return NonStaticResource.class;
+        }
+    }
+
+    @Path("/non-public")
+    static class NonPublicResource {
+
+        @GET
+        public String get() {
+            return "hi";
+        }
+    }
+
+    @Path("/non-public-sub")
+    public static class NonPublicResourceSubResource {
+
+        @Path("class")
+        public Class<NonPublicResource> getClazz() {
+            return NonPublicResource.class;
+        }
+    }
+
+    @Path("/abstract")
+    public abstract static class AbstractResource {
+
+        @GET
+        public String get() {
+            return "Hi!";
+        }
+    }
+
+    @Path("/abstract-sub")
+    public static class AbstractResourceSubResource {
+
+        @Path("class")
+        public Class<AbstractResource> getClazz() {
+            return AbstractResource.class;
+        }
+    }
+
+    @Path("interface")
+    public static interface InterfaceResource {
+    }
+
+    @Path("interface-sub")
+    public static class InterfaceResourceSubResource {
+
+        @Path("class")
+        public Class<InterfaceResource> getClazz() {
+            return InterfaceResource.class;
+        }
+    }
+
+    @Path("non-public-with-constructor")
+    static class NonPublicResourceWithConstructor {
+
+        public NonPublicResourceWithConstructor() {
+        }
+
+        @GET
+        public String get() {
+            return "Hi!";
+        }
+    }
+
+    @Path("non-public-sub-with-constructor")
+    public static class NonPublicResourceSubResourceWithConstructor {
+
+        @Path("class")
+        public Class<NonPublicResourceWithConstructor> getClazz() {
+            return NonPublicResourceWithConstructor.class;
+        }
+    }
+
+    @Path("public-with-private-constructor")
+    public static class PublicResourceWithPrivateConstructor {
+
+        private PublicResourceWithPrivateConstructor() {
+        }
+
+        @GET
+        public String get() {
+            return "Hi!";
+        }
+    }
+
+    @Path("provider-resource")
+    public static class ProviderResource {
+
+        @GET
+        public String get() {
+            return "Hi!";
+        }
+    }
+
+    @Provider
+    public class NonStaticProvider implements MessageBodyWriter<String> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(s.getBytes());
+        }
+    }
+
+    @Provider
+    static class NonPublicProvider implements MessageBodyWriter<String> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            final String wrapped = ">> " + s + " <<";
+            entityStream.write(wrapped.getBytes());
+        }
+    }
+
+    @Provider
+    static class PublicProviderWithPrivateConstructor implements MessageBodyWriter<String> {
+
+        private PublicProviderWithPrivateConstructor() {
+        }
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(s.getBytes());
+        }
+    }
+
+    @Provider
+    public abstract static class AbstractProvider implements MessageBodyWriter<String> {
+
+        @Override
+        public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                                   final MediaType mediaType) {
+            return type == String.class;
+        }
+
+        @Override
+        public long getSize(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(final String s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                            final OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write(s.getBytes());
+        }
+    }
+
+    // Inner test classes - each needed resource config variation has its own inner class
+
+    public static class NonStaticResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+            return new ResourceConfig(NonStaticResource.class);
+        }
+
+        @Test
+        public void testNonStaticResource() throws IOException {
+            final List<LogRecord> loggedRecords = getLoggedRecords();
+            boolean firstFound = false;
+            boolean secondFound = false;
+            for (LogRecord record : loggedRecords) {
+                if (record.getMessage().contains("cannot be instantiated and will be ignored")) {
+                    firstFound = true;
+                } else if (record.getMessage().contains("is empty. It has no resource")) {
+                    secondFound = true;
+                }
+                if (firstFound && secondFound) {
+                    break;
+                }
+            }
+            assertTrue("Expected log message (1st) was not found.", firstFound);
+            assertTrue("Expected log message (2nd) was not found.", secondFound);
+        }
+    }
+
+    public static class NonStaticResourceSubResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(NonStaticResourceSubResource.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testNonStaticResource() throws IOException {
+            target().path("/non-static-sub/class").request().get(String.class);
+        }
+    }
+
+    public static class NonPublicResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(NonPublicResource.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testNonPublicResource() throws IOException {
+            target().path("/non-public").request().get(String.class);
+        }
+    }
+
+    public static class NonPublicResourceSubResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(NonPublicResourceSubResource.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testNonPublicResource() throws IOException {
+            target().path("/non-public-sub/class").request().get(String.class);
+        }
+    }
+
+    public static class AbstractResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+            return new ResourceConfig(AbstractResource.class);
+        }
+
+        @Test
+        public void testAbstractResource() throws IOException {
+            final List<LogRecord> loggedRecords = getLoggedRecords();
+            boolean found = false;
+            for (LogRecord record : loggedRecords) {
+                if (record.getMessage().contains("cannot be instantiated and will be ignored")) {
+                    found = true;
+                    break;
+                }
+            }
+            assertTrue("Expected log record was not found.", found);
+        }
+    }
+
+    public static class AbstractResourceSubResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(AbstractResourceSubResource.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testAbstractResourceSubResource() throws IOException {
+            target().path("abstract-sub/class").request().get(String.class);
+        }
+    }
+
+    public static class InterfaceResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+            return new ResourceConfig(InterfaceResource.class);
+        }
+
+        @Test
+        public void testInterfaceResource() throws IOException {
+            final List<LogRecord> loggedRecords = getLoggedRecords();
+            boolean firstFound = false;
+            boolean secondFound = false;
+            for (LogRecord record : loggedRecords) {
+                if (record.getMessage().contains("cannot be instantiated and will be ignored")) {
+                    firstFound = true;
+                } else if (record.getMessage().contains("is empty. It has no resource")) {
+                    secondFound = true;
+                }
+                if (firstFound && secondFound) {
+                    break;
+                }
+            }
+            assertTrue("Expected log message (1st) was not found.", firstFound);
+            assertTrue("Expected log message (2nd) was not found.", secondFound);
+        }
+    }
+
+    public static class InterfaceResourceSubResourceTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(InterfaceResourceSubResource.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testInterfaceResourceSubResource() throws IOException {
+            target().path("interface-sub/class").request().get(String.class);
+        }
+    }
+
+    public static class NonPublicResourceWithConstructorTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(NonPublicResourceWithConstructor.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testNonPublicResourceWithConstructor() throws IOException {
+            target().path("non-public-with-constructor").request().get(String.class);
+        }
+    }
+
+    public static class NonPublicResourceSubresourceWithConstructorTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(NonPublicResourceSubResourceWithConstructor.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testNonPublicResourceSubResourceWithConstructor() throws IOException {
+            target().path("non-public-sub-with-constructor/class").request().get(String.class);
+        }
+    }
+
+    public static class PublicResourceWithPrivateConstructorTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+            return new ResourceConfig(PublicResourceWithPrivateConstructor.class);
+        }
+
+        @Test(expected = InternalServerErrorException.class)
+        public void testPublicResourceWithPrivateConstructor() throws IOException {
+            target().path("public-with-private-constructor").request().get(String.class);
+        }
+    }
+
+    public static class NonStaticProviderTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+            return new ResourceConfig(ProviderResource.class, NonStaticProvider.class);
+        }
+
+        @Test
+        public void testNonStaticProvider() throws IOException {
+            final List<LogRecord> loggedRecords = getLoggedRecords();
+            boolean found = false;
+            for (LogRecord record : loggedRecords) {
+                if (record.getMessage().contains("Instantiation of non-static member classes is not supported")) {
+                    found = true;
+                    break;
+                }
+            }
+            assertTrue("Expected log record was not found.", found);
+        }
+    }
+
+    public static class NonPublicProviderTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            return new ResourceConfig(ProviderResource.class, NonPublicProvider.class);
+        }
+
+        @Test
+        public void testNonPublicProvider() throws IOException {
+            // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+            // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+            // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+            // compatibility.
+            final String s = target().path("provider-resource").request("text/html").get(String.class);
+            assertEquals(">> Hi! <<", s);
+        }
+    }
+
+    // NOTE - the original Jersey 1 test nonPublicProviderWithConstructor does not make sense considering that it works even
+    // w/o constructor
+
+    public static class PublicProviderWithPrivateConstructorTest extends JerseyTest {
+
+        @Override
+        public ResourceConfig configure() {
+            set(TestProperties.RECORD_LOG_LEVEL, Level.WARNING.intValue());
+            return new ResourceConfig(ProviderResource.class, PublicProviderWithPrivateConstructor.class);
+        }
+
+        @Test
+        public void testNonPublicProvider() throws IOException {
+            final List<LogRecord> loggedRecords = getLoggedRecords();
+            boolean found = false;
+            for (LogRecord record : loggedRecords) {
+                if (record.getMessage().contains("Could not find a suitable constructor")) {
+                    found = true;
+                    break;
+                }
+            }
+            assertTrue("Expected log record was not found.", found);
+        }
+    }
+
+    // abstract provider - throws MultiException after calling configure(), but before running the @Test method
+    // the same for interface provider test
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ProvidersLegacyOrderingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ProvidersLegacyOrderingTest.java
new file mode 100644
index 0000000..012dc88
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ProvidersLegacyOrderingTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.message.MessageProperties;
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ProvidersLegacyOrderingTest extends JerseyTest {
+
+    @Produces("application/xml")
+    public static class MyMBW5 implements MessageBodyWriter<Object> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return false;
+        }
+
+        @Override
+        public long getSize(Object myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(Object myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+        }
+    }
+
+    @Produces("application/*")
+    public static class MyMBW6 implements MessageBodyWriter<Object> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return false;
+        }
+
+        @Override
+        public long getSize(Object myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(Object myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+        }
+    }
+
+    @Path("/")
+    public static class MyResource {
+
+        @Context
+        MessageBodyWorkers messageBodyWorkers;
+
+        @GET
+        public String getMyType() {
+
+            final Map<MediaType, List<MessageBodyWriter>> writers = messageBodyWorkers.getWriters(MediaType.APPLICATION_XML_TYPE);
+            final List<MessageBodyWriter> messageBodyWriters1 = writers.get(MediaType.APPLICATION_XML_TYPE);
+            final List<MessageBodyWriter> messageBodyWriters2 = writers
+                    .get(MediaTypes.getTypeWildCart(MediaType.APPLICATION_XML_TYPE));
+
+            // custom provider has priority - it has to be first
+            assertTrue(messageBodyWriters1.get(0) instanceof MyMBW5);
+            assertTrue(messageBodyWriters2.get(0) instanceof MyMBW6);
+
+            return "";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig resourceConfig = new ResourceConfig(MyResource.class, MyMBW5.class, MyMBW6.class);
+        resourceConfig.property(MessageProperties.LEGACY_WORKERS_ORDERING, true);
+        return resourceConfig;
+    }
+
+    @Test
+    public void orderingTest() {
+        try {
+            Response response = target().request("application/test1").get(Response.class);
+
+            assertNotNull(response);
+            assertEquals("Request was not handled correctly, most likely fault in MessageBodyWorker selection.",
+                    200, response.getStatus());
+        } catch (Exception e) {
+            fail("Request was not handled correctly, most likely fault in MessageBodyWorker selection.");
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ProvidersOrderingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ProvidersOrderingTest.java
new file mode 100644
index 0000000..0d5b748
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ProvidersOrderingTest.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ProvidersOrderingTest extends JerseyTest {
+
+    public static class MyType {
+    }
+
+    public static class MyTypeExt extends MyType {
+    }
+
+    public static class MyTypeExtExt extends MyTypeExt {
+    }
+
+    @Produces("application/test1")
+    public static class MyMBW1 implements MessageBodyWriter<MyType> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBW1(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return false;
+        }
+
+        @Override
+        public long getSize(MyType myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(MyType myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+        }
+    }
+
+    @Produces("application/*")
+    public static class MyMBW2 implements MessageBodyWriter<MyType> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBW2(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return false;
+        }
+
+        @Override
+        public long getSize(MyType myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(MyType myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+        }
+    }
+
+    @Produces("*/*")
+    public static class MyMBW3 implements MessageBodyWriter<MyType> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBW3(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return true;
+        }
+
+        @Override
+        public long getSize(MyType myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(MyType myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+            entityStream.write("test".getBytes());
+        }
+    }
+
+    @Produces("application/*")
+    public static class MyMBW4 implements MessageBodyWriter<MyTypeExt> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBW4(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return false;
+        }
+
+        @Override
+        public long getSize(MyTypeExt myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(MyTypeExt myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+        }
+    }
+
+    @Produces("application/xml")
+    public static class MyMBW5 implements MessageBodyWriter<Object> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return false;
+        }
+
+        @Override
+        public long getSize(Object myType, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(Object myType,
+                            Class<?> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders,
+                            OutputStream entityStream) throws IOException, WebApplicationException {
+        }
+    }
+
+    @Consumes("application/test")
+    public static class MyMBR1 implements MessageBodyReader<MyType> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBR1(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return false;
+        }
+
+        @Override
+        public MyType readFrom(Class<MyType> type,
+                               Type genericType,
+                               Annotation[] annotations,
+                               MediaType mediaType,
+                               MultivaluedMap<String, String> httpHeaders,
+                               InputStream entityStream) throws IOException, WebApplicationException {
+            return null;
+        }
+    }
+
+    @Consumes("application/test1")
+    public static class MyMBR2 implements MessageBodyReader<MyTypeExt> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBR2(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return false;
+        }
+
+        @Override
+        public MyTypeExt readFrom(Class<MyTypeExt> type,
+                                  Type genericType,
+                                  Annotation[] annotations,
+                                  MediaType mediaType,
+                                  MultivaluedMap<String, String> httpHeaders,
+                                  InputStream entityStream) throws IOException, WebApplicationException {
+            return null;
+        }
+    }
+
+    @Consumes("application/test1")
+    public static class MyMBR3 implements MessageBodyReader<MyTypeExtExt> {
+
+        private final List<Class<?>> callList;
+
+        public MyMBR3(List<Class<?>> callList) {
+            this.callList = callList;
+        }
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            callList.add(this.getClass());
+            return true;
+        }
+
+        @Override
+        public MyTypeExtExt readFrom(Class<MyTypeExtExt> type,
+                                     Type genericType,
+                                     Annotation[] annotations,
+                                     MediaType mediaType,
+                                     MultivaluedMap<String, String> httpHeaders,
+                                     InputStream entityStream) throws IOException, WebApplicationException {
+            return new MyTypeExtExt();
+        }
+    }
+
+    @Path("/")
+    public static class MyResource {
+
+        @Context
+        MessageBodyWorkers messageBodyWorkers;
+
+        @PUT
+        @Consumes("application/test1")
+        public MyType getMyType(MyTypeExtExt myType) {
+
+            final Map<MediaType, List<MessageBodyWriter>> writers = messageBodyWorkers.getWriters(MediaType.APPLICATION_XML_TYPE);
+            final List<MessageBodyWriter> messageBodyWriters = writers.get(MediaType.APPLICATION_XML_TYPE);
+
+            assertTrue(messageBodyWriters.get(0) instanceof MyMBW5);
+
+            return myType;
+        }
+
+        @Path("bytearray")
+        @POST
+        public byte[] bytearray(byte[] bytes) {
+            return bytes;
+        }
+    }
+
+    private List<Class<?>> callList;
+
+    @Override
+    protected Application configure() {
+        callList = new ArrayList<>();
+
+        final ResourceConfig resourceConfig = new ResourceConfig(MyResource.class, MyMBW5.class);
+        resourceConfig.registerInstances(new MyMBW1(callList), new MyMBW2(callList), new MyMBW3(callList), new MyMBW4(callList),
+                new MyMBR1(callList), new MyMBR2(callList), new MyMBR3(callList), new MyByteArrayProvider());
+        return resourceConfig;
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void orderingTest() {
+        callList.clear();
+        WebTarget target = target();
+
+        try {
+            Response response = target.request("application/test1")
+                    .put(Entity.entity("test", "application/test1"), Response.class);
+
+            assertNotNull(response);
+            assertEquals("Request was not handled correctly, most likely fault in MessageBodyWorker selection.",
+                    200, response.getStatus());
+        } catch (Exception e) {
+            System.out.println(e.getMessage());
+            e.printStackTrace();
+            fail("Request was not handled correctly, most likely fault in MessageBodyWorker selection.");
+        }
+
+        List<Class<?>> classes = Arrays.asList(
+                MyMBR3.class, // MBR - smallest type distance (MBR1 and MBR2 are not called because of that)
+                MyMBW4.class, // MBW - type distance
+                MyMBW1.class, // MBW - most specific media type application/test1
+                MyMBW2.class, // MBW - application/*
+                MyMBW3.class  // MBW - */*, first usable writer (returns true to isWriteable call)
+        );
+
+        assertEquals(classes, callList);
+    }
+
+    @Test
+    public void replaceBuiltInProvider() {
+        callList.clear();
+        WebTarget target = target("bytearray");
+
+        try {
+            Response response = target.request().post(Entity.text("replaceBuiltInProvider"), Response.class);
+
+            assertNotNull(response);
+            assertEquals("Request was not handled correctly, most likely fault in MessageBodyWorker selection.",
+                    200, response.getStatus());
+        } catch (Exception e) {
+            System.out.println(e.getMessage());
+            e.printStackTrace();
+            fail("Request was not handled correctly, most likely fault in MessageBodyWorker selection.");
+        }
+
+        assertTrue(MyByteArrayProvider.counter == 2); // used to read and write entity on server side.
+
+    }
+
+    @Produces({"application/octet-stream", "*/*"})
+    @Consumes({"application/octet-stream", "*/*"})
+    public static final class MyByteArrayProvider extends AbstractMessageReaderWriterProvider<byte[]> {
+
+        public static int counter = 0;
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == byte[].class;
+        }
+
+        @Override
+        public byte[] readFrom(
+                Class<byte[]> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, String> httpHeaders,
+                InputStream entityStream) throws IOException {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            writeTo(entityStream, out);
+            counter++;
+            return out.toByteArray();
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return type == byte[].class;
+        }
+
+        @Override
+        public void writeTo(
+                byte[] t,
+                Class<?> type,
+                Type genericType,
+                Annotation annotations[],
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException {
+            counter++;
+            entityStream.write(t);
+        }
+
+        @Override
+        public long getSize(byte[] t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return t.length;
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/QueryParamEncodingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/QueryParamEncodingTest.java
new file mode 100644
index 0000000..a5991d9
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/QueryParamEncodingTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Miroslav Fuksa
+ *
+ */
+public class QueryParamEncodingTest extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Path("resource")
+    public static class TestResource {
+        @GET
+        @Path("encoded")
+        public String getEncoded(@Encoded @QueryParam("query") String queryParam) {
+            return queryParam.equals("%25dummy23%2Ba") + ":" + queryParam;
+        }
+
+        @GET
+        @Path("decoded")
+        public String getDecoded(@QueryParam("query") String queryParam) {
+            return queryParam.equals("%dummy23+a") + ":" + queryParam;
+        }
+    }
+
+    @Test
+    public void testEncoded() {
+        final Response response = target().path("resource/encoded").queryParam("query", "%dummy23+a").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("true:%25dummy23%2Ba", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testDecoded() {
+        final Response response = target().path("resource/decoded").queryParam("query", "%dummy23+a").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("true:%dummy23+a", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ReaderProviderTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ReaderProviderTest.java
new file mode 100644
index 0000000..02392bcd
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ReaderProviderTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2012, 2018 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.common;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Testing {@link Reader} on client and server.
+ *
+ * @author Miroslav Fuksa
+ */
+public class ReaderProviderTest extends JerseyTest {
+
+    public static final String GET_READER_RESPONSE = "GET_READER_RESPONSE";
+    public static final String GET_POST_RESPONSE = "GET_POST_RESPONSE";
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(ReaderResource.class);
+    }
+
+    @Test
+    public void testReader() {
+        Response response = target().path("test/postReaderGetReader").request().post(Entity.entity(GET_POST_RESPONSE,
+                MediaType.TEXT_PLAIN));
+        assertEquals(200, response.getStatus());
+        assertEquals(GET_POST_RESPONSE, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testGetReader() {
+        Response response = target().path("test/getReader").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals(GET_READER_RESPONSE, response.readEntity(String.class));
+    }
+
+    @Test
+    public void testEmptyReader() throws IOException {
+        Response response = target().path("test/getEmpty").request().get();
+        assertEquals(204, response.getStatus());
+        final Reader reader = response.readEntity(Reader.class);
+        assertNotNull(reader);
+        assertEquals(-1, reader.read());
+    }
+
+    @Test
+    public void testReaderOnClientAsResponseEntity() throws IOException {
+        Response response = target().path("test/getReader").request().get();
+        assertEquals(200, response.getStatus());
+        final Reader reader = response.readEntity(Reader.class);
+        assertNotNull(reader);
+        BufferedReader br = new BufferedReader(reader);
+        assertEquals(GET_READER_RESPONSE, br.readLine());
+    }
+
+    @Test
+    public void testReaderOnClientAsRequestEntity() throws IOException {
+        Response response = target().path("test/postReaderGetReader").request()
+                .post(Entity.entity(new StringReader(GET_POST_RESPONSE), MediaType.TEXT_PLAIN));
+        assertEquals(200, response.getStatus());
+        assertEquals(GET_POST_RESPONSE, response.readEntity(String.class));
+    }
+
+    @Path("test")
+    public static class ReaderResource {
+
+        @POST
+        @Path("postReaderGetReader")
+        public Reader postReader(Reader reader) throws IOException {
+            return reader;
+        }
+
+        @GET
+        @Path("getReader")
+        public Reader getReader() throws IOException {
+            return new StringReader(GET_READER_RESPONSE);
+        }
+
+        @GET
+        @Path("getEmpty")
+        public String getemptyResponse() throws IOException {
+            return null;
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/RequestScopeTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/RequestScopeTest.java
new file mode 100644
index 0000000..99a03f0
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/RequestScopeTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 2018 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.common;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.DisposableSupplier;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * E2E Request Scope Tests.
+ *
+ * @author Michal Gajdos
+ */
+@Ignore("Test Supplier Injection -> this test require dispose() method from Factory")
+public class RequestScopeTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(RemoveResource.class)
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bindFactory(CloseMeFactory.class, RequestScoped.class).to(CloseMe.class).in(RequestScoped.class);
+                    }
+                });
+    }
+
+    public interface CloseMe {
+
+        String eval();
+
+        void close();
+    }
+
+    public static class CloseMeFactory implements DisposableSupplier<CloseMe> {
+
+        private static final CountDownLatch CLOSED_LATCH = new CountDownLatch(1);
+
+        @Override
+        public CloseMe get() {
+            return new CloseMe() {
+                @Override
+                public String eval() {
+                    return "foo";
+                }
+
+                @Override
+                public void close() {
+                    CLOSED_LATCH.countDown();
+                }
+            };
+        }
+
+        @Override
+        public void dispose(final CloseMe instance) {
+            instance.close();
+        }
+    }
+
+    @Path("remove")
+    public static class RemoveResource {
+
+        private CloseMe closeMe;
+
+        @Inject
+        public RemoveResource(final CloseMe closeMe) {
+            this.closeMe = closeMe;
+        }
+
+        @GET
+        public String get() {
+            return closeMe.eval();
+        }
+    }
+
+    /**
+     * Test that Factory.dispose method is called during release of Request Scope.
+     */
+    @Test
+    public void testRemove() throws Exception {
+        final Response response = target().path("remove").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("foo"));
+
+        assertTrue(CloseMeFactory.CLOSED_LATCH.await(3, TimeUnit.SECONDS));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ResponseLinksTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ResponseLinksTest.java
new file mode 100644
index 0000000..941b3aa
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/ResponseLinksTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import java.net.URI;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Libor Kamolis (libor.kramolis at oracle.com)
+ */
+public class ResponseLinksTest extends JerseyTest {
+
+    @Path("/test")
+    public static class MyResource {
+
+        @Context
+        private UriInfo uriInfo;
+
+        /**
+         * Reproducer for JERSEY-2168
+         */
+        @Path("1")
+        @GET
+        @Produces({MediaType.APPLICATION_JSON})
+        public Response getLink() {
+            URI link = uriInfo.getAbsolutePathBuilder().queryParam("limit", 50).build();
+            return Response.status(Response.Status.OK).link(link, "prev").build();
+        }
+
+        /**
+         * Reproducer for JERSEY-2168
+         */
+        @Path("2")
+        @GET
+        @Produces({MediaType.APPLICATION_JSON})
+        public Response getLinks() {
+            Link link1 = Link.fromUri(uriInfo.getAbsolutePathBuilder().queryParam("limit", 50).build())
+                    .rel("prev").build();
+            Link link2 = Link.fromUri(
+                    uriInfo.getAbsolutePathBuilder().queryParam("limit", 50).queryParam("action", "next").build()).rel("next")
+                    .title("next page").build();
+            return Response.status(Response.Status.OK).links(link1, link2).build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(MyResource.class);
+        resourceConfig.register(LoggingFeature.class);
+        return resourceConfig;
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(LoggingFeature.class);
+        super.configureClient(config);
+    }
+
+    /**
+     * Reproducer for JERSEY-2168
+     */
+    @Test
+    public void testGetLink() {
+        WebTarget target = target("test");
+        Response response = target.path("1").request(MediaType.APPLICATION_JSON).get();
+        Set<Link> links = response.getLinks();
+        Assert.assertEquals(1, links.size());
+        Assert.assertNotNull(response.getLink("prev"));
+        Assert.assertTrue(response.getLink("prev").getUri().toString().endsWith("1?limit=50"));
+    }
+
+    /**
+     * Reproducer for JERSEY-2168
+     */
+    @Test
+    public void testGetLinks() {
+        WebTarget target = target("test");
+        Response response = target.path("2").request(MediaType.APPLICATION_JSON).get();
+        Set<Link> links = response.getLinks();
+        Assert.assertEquals(2, links.size());
+        Assert.assertNotNull(response.getLink("prev"));
+        Assert.assertTrue(response.getLink("prev").getUri().toString().endsWith("2?limit=50"));
+        Assert.assertNotNull(response.getLink("next"));
+        Assert.assertEquals("next page", response.getLink("next").getTitle());
+        Assert.assertTrue(response.getLink("next").getUri().toString().endsWith("2?limit=50&action=next"));
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/UriComponentTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/UriComponentTest.java
new file mode 100644
index 0000000..5d44b60
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/UriComponentTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2013, 2018 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.common;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.uri.UriComponent;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Libor Kamolis (libor.kramolis at oracle.com)
+ */
+public class UriComponentTest extends JerseyTest {
+
+    private static final String VALUE_PREFIX = "-<#[";
+    private static final String VALUE_SUFFIX = "]#>-";
+
+    @Path("/test")
+    public static class MyResource {
+        @GET
+        @Path("text")
+        public String getTextValue(@QueryParam("text") String text) {
+            return VALUE_PREFIX + text + VALUE_SUFFIX;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(MyResource.class);
+        resourceConfig.register(LoggingFeature.class);
+        return resourceConfig;
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(LoggingFeature.class);
+        super.configureClient(config);
+    }
+
+    /**
+     * Reproducer for JERSEY-2260
+     */
+    @Test
+    public void testText() {
+        final String QUERY_RESERVED_CHARS = ";/?:@&=+,$";
+        final String OTHER_SPECIAL_CHARS = "\"\t- \n'";
+
+        testTextImpl("query reserved characters", QUERY_RESERVED_CHARS);
+        testTextImpl("other special characters", OTHER_SPECIAL_CHARS);
+
+        testTextImpl("query reserved characters between template brackets", "{abc" + QUERY_RESERVED_CHARS + "XYZ}");
+        testTextImpl("other special characters between template brackets", "{abc" + OTHER_SPECIAL_CHARS + "XYZ}");
+
+        testTextImpl("json - double quote", "{ \"jmeno\" : \"hodnota\" }");
+        testTextImpl("json - single quote", "{ 'jmeno' : 'hodnota' }");
+    }
+
+    private void testTextImpl(String message, final String text) {
+        String encoded = UriComponent.encode(text, UriComponent.Type.QUERY_PARAM_SPACE_ENCODED);
+        WebTarget target = target("test/text");
+        Response response = target.queryParam("text", encoded).request().get();
+        Assert.assertEquals(200, response.getStatus());
+        String actual = response.readEntity(String.class);
+        Assert.assertEquals(message, VALUE_PREFIX + text + VALUE_SUFFIX, actual);
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/package-info.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/package-info.java
new file mode 100644
index 0000000..05d9d55
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012, 2018 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
+ */
+
+/**
+ * Jersey End to End common test classes.
+ */
+package org.glassfish.jersey.tests.e2e.common;
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/AbstractSlashesWithContextPathTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/AbstractSlashesWithContextPathTest.java
new file mode 100644
index 0000000..d5c4168
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/AbstractSlashesWithContextPathTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015, 2018 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.container;
+
+import java.net.URI;
+
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+
+/**
+ * Abstract Jersey test with prepared resources for URL match
+ * testing on different containers.
+ *
+ * @author Petr Bouda
+ */
+public class AbstractSlashesWithContextPathTest extends JerseyTest {
+
+    public static final String CONTAINER_RESPONSE = "Container-Response";
+    public static final String CONTEXT_PATH = "base";
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(SimpleResource.class,
+                PathParamResource.class,
+                EmptyResource.class,
+                EmptyPathParamResource.class);
+
+        resourceConfig.property(ServerProperties.REDUCE_CONTEXT_PATH_SLASHES_ENABLED, true);
+        return resourceConfig;
+    }
+
+    @Path("simple")
+    public static class SimpleResource {
+
+        @GET
+        public String encoded() {
+            return CONTAINER_RESPONSE;
+        }
+
+    }
+
+    @Path("pathparam")
+    public static class PathParamResource {
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/test")
+        public String hello(@PathParam("bar") final String bar, @PathParam("baz") final String baz) {
+            return bar + "-" + baz;
+        }
+    }
+
+    @Path("/")
+    public static class EmptyPathParamResource {
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/test")
+        public String hello(@PathParam("bar") final String bar, @PathParam("baz") final String baz) {
+            return bar + "-" + baz;
+        }
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/testParams")
+        public String helloWithQueryParams(@PathParam("bar") final String bar,
+                                           @PathParam("baz") final String baz,
+                                           @QueryParam("bar") final String queryBar,
+                                           @QueryParam("baz") final String queryBaz) {
+            return "PATH PARAM: " + bar + "-" + baz + ", QUERY PARAM " + queryBar + "-" + queryBaz;
+        }
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/encoded")
+        public String getEncoded(@Encoded @QueryParam("query") String queryParam) {
+            return queryParam.equals("%25dummy23%2Ba") + ":" + queryParam;
+        }
+    }
+
+    @Path("/")
+    public static class EmptyResource {
+
+        @GET
+        @Path("/test")
+        public String hello() {
+            return CONTAINER_RESPONSE;
+        }
+    }
+
+    protected Response call(String path) {
+        URI hostPort = UriBuilder.fromUri("http://localhost").port(getPort()).build();
+        return client().target(hostPort).path(path).request().get();
+    }
+
+    /**
+     * Context path configuration
+     */
+    @Override
+    protected URI getBaseUri() {
+        URI baseUri = super.getBaseUri();
+        return UriBuilder.fromUri(baseUri).path(CONTEXT_PATH).build();
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/EscapedUriTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/EscapedUriTest.java
new file mode 100644
index 0000000..29aa2c9
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/EscapedUriTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EscapedUriTest extends JerseyContainerTest {
+
+    private static final String RESPONSE = "CONTENT";
+
+    @Path("x%20y")
+    public static class EscapedUriResource {
+
+        private final String context;
+
+        @SuppressWarnings("UnusedDeclaration")
+        public EscapedUriResource() {
+            this("");
+        }
+
+        public EscapedUriResource(final String context) {
+            this.context = context;
+        }
+
+        @GET
+        public String get(@Context final UriInfo info) {
+            assertEquals(context + "/x%20y", info.getAbsolutePath().getRawPath());
+            assertEquals("/", info.getBaseUri().getRawPath());
+            assertEquals(context + "/x y", "/" + info.getPath());
+            assertEquals(context + "/x%20y", "/" + info.getPath(false));
+
+            return RESPONSE;
+        }
+    }
+
+    @Path("non/x y")
+    public static class NonEscapedUriResource extends EscapedUriResource {
+
+        public NonEscapedUriResource() {
+            super("/non");
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(EscapedUriResource.class, NonEscapedUriResource.class);
+    }
+
+    @Test
+    public void testEscaped() {
+        assertThat(target("x%20y").request().get(String.class), is(RESPONSE));
+    }
+
+    @Test
+    public void testNonEscaped() {
+        assertThat(target("non/x y").request().get(String.class), is(RESPONSE));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/GrizzlySlashesWithContextPathTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/GrizzlySlashesWithContextPathTest.java
new file mode 100644
index 0000000..509fdcf
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/GrizzlySlashesWithContextPathTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015, 2018 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.container;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Test Jersey container implementation of URL resolving.
+ * Slashes before a context path can be omitted depending on
+ * the given property.
+ *
+ * @author Petr Bouda
+ */
+public class GrizzlySlashesWithContextPathTest extends AbstractSlashesWithContextPathTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new GrizzlyTestContainerFactory();
+    }
+
+    @Test
+    public void testSimpleSlashes() {
+        Response result = call(CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+
+        result = call("/" + CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+
+        result = call("//" + CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+
+        result = call("///" + CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+
+        result = call("////" + CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithPathParam() {
+        Response result = call("//" + CONTEXT_PATH + "/pathparam/Container/Response/test");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithEmptyPathParam() {
+        Response result = call("//" + CONTEXT_PATH + "/pathparam///test");
+        assertEquals("-", result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmptyPathParam() {
+        Response result = call("//" + CONTEXT_PATH + "///test");
+        assertEquals("-", result.readEntity(String.class));
+    }
+
+    @Test
+    public void testEncodedQueryParams() {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        WebTarget target = client().target(hostPort).path("//" + CONTEXT_PATH + "///encoded")
+                .queryParam("query", "%dummy23+a");
+
+        Response response = target.request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("true:%25dummy23%2Ba", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmptyPathParamWithQueryParams() {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        WebTarget target = client().target(hostPort).path("//" + CONTEXT_PATH + "///testParams")
+                .queryParam("bar", "Container")
+                .queryParam("baz", "Response");
+
+        Response result = target.request().get();
+        assertEquals("PATH PARAM: -, QUERY PARAM Container-Response", result.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/GzipContentEncodingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/GzipContentEncodingTest.java
new file mode 100644
index 0000000..61c463e
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/GzipContentEncodingTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import java.io.IOException;
+import java.util.zip.GZIPInputStream;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.GZipEncoder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.filter.EncodingFilter;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class GzipContentEncodingTest extends JerseyContainerTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @POST
+        public String post(final String content) {
+            return content;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, EncodingFilter.class, GZipEncoder.class);
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(new ReaderInterceptor() {
+            @Override
+            public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOException, WebApplicationException {
+                context.setInputStream(new GZIPInputStream(context.getInputStream()));
+                return context.proceed();
+            }
+        });
+    }
+
+    @Test
+    public void testGet() {
+        final Response response = target().request()
+                .header(HttpHeaders.ACCEPT_ENCODING, "gzip")
+                .get();
+
+        assertThat(response.readEntity(String.class), is("GET"));
+    }
+
+    @Test
+    public void testPost() {
+        final Response response = target().request()
+                .header(HttpHeaders.ACCEPT_ENCODING, "gzip")
+                .post(Entity.text("POST"));
+
+        assertThat(response.readEntity(String.class), is("POST"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/HeadTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/HeadTest.java
new file mode 100644
index 0000000..a143d0f
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/HeadTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public class HeadTest extends JerseyContainerTest {
+
+    private static final List<TestContainerFactory> FACTORIES = Arrays.asList(
+            new GrizzlyTestContainerFactory(),
+            new InMemoryTestContainerFactory(),
+            new SimpleTestContainerFactory(),
+            new JettyTestContainerFactory());
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<TestContainerFactory[]> parameters() throws Exception {
+        return FACTORIES.stream().map(input -> new TestContainerFactory[]{input}).collect(Collectors.toList());
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @Path("string")
+        @GET
+        public String getString() {
+            return "GET";
+        }
+
+        @Path("byte")
+        @GET
+        public byte[] getByte() {
+            return "GET".getBytes();
+        }
+
+        @Path("ByteArrayInputStream")
+        @GET
+        public InputStream getInputStream() {
+            return new ByteArrayInputStream("GET".getBytes());
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testHeadString() {
+        _testHead("string", MediaType.TEXT_PLAIN_TYPE);
+    }
+
+    @Test
+    public void testHeadByte() {
+        _testHead("byte", MediaType.APPLICATION_OCTET_STREAM_TYPE);
+    }
+
+    @Test
+    public void testHeadByteArrayInputStream() {
+        _testHead("ByteArrayInputStream", MediaType.APPLICATION_OCTET_STREAM_TYPE);
+    }
+
+    private void _testHead(final String path, final MediaType mediaType) {
+        final Response response = target(path).request(mediaType).head();
+        assertThat(response.getStatus(), is(200));
+
+        final String lengthStr = response.getHeaderString(HttpHeaders.CONTENT_LENGTH);
+        assertThat(lengthStr, notNullValue());
+        assertThat(Integer.parseInt(lengthStr), is(3));
+        assertThat(response.getMediaType(), is(mediaType));
+        assertFalse(response.hasEntity());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JdkSlashesWithContextPathTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JdkSlashesWithContextPathTest.java
new file mode 100644
index 0000000..b2276f4
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JdkSlashesWithContextPathTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2015, 2018 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.container;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Test Jersey container implementation of URL resolving.
+ * JDK Server does not support multiple slashes before the context-path,
+ * in case of multiple slashes server does not match the processing handler and
+ * therefore Jersey Request cannot be created and processed.
+ *
+ * @author Petr Bouda
+ */
+public class JdkSlashesWithContextPathTest extends AbstractSlashesWithContextPathTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new JdkHttpServerTestContainerFactory();
+    }
+
+    @Test
+    public void testSimpleSlashes() {
+        Response result = call(CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+
+        result = call("/" + CONTEXT_PATH + "/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithPathParam() {
+        Response result = call(CONTEXT_PATH + "/pathparam/Container/Response/test");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithEmptyPathParam() {
+        Response result = call(CONTEXT_PATH + "/pathparam///test");
+        assertEquals("-", result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmptyPathParam() {
+        Response result = call(CONTEXT_PATH + "///test");
+        assertEquals("-", result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmptyPathParamWithQueryParams() {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        WebTarget target = client().target(hostPort).path(CONTEXT_PATH + "///testParams")
+                .queryParam("bar", "Container")
+                .queryParam("baz", "Response");
+
+        Response result = target.request().get();
+        assertEquals("PATH PARAM: -, QUERY PARAM Container-Response", result.readEntity(String.class));
+    }
+
+    @Test
+    @Ignore("failing with JDK container.")
+    public void testEncodedQueryParams() {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        WebTarget target = client().target(hostPort).path("//" + CONTEXT_PATH + "///encoded")
+                .queryParam("query", "%dummy23+a");
+
+        Response response = target.request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("true:%25dummy23%2Ba", response.readEntity(String.class));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java
new file mode 100644
index 0000000..17c8c67
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/Jersey2462Test.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2013, 2018 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.container;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer tests for JERSEY-2462 on Grizzly, Jetty and Simple HTTP server.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({Jersey2462Test.GrizzlyContainerTest.class,
+        Jersey2462Test.JettyContainerTest.class,
+        Jersey2462Test.SimpleContainerTest.class})
+public class Jersey2462Test {
+    private static final String REQUEST_NUMBER = "request-number";
+
+    /**
+     * Test resource.
+     *
+     * @author Marek Potociar (marek.potociar at oracle.com)
+     */
+    @Path("echo")
+    public static class EchoResource {
+
+        /**
+         * Echoes the input message.
+         *
+         * @param message input message.
+         * @return echoed input message.
+         */
+        @POST
+        @Consumes("text/plain")
+        @Produces("text/plain")
+        public String echo(String message) {
+            return message + "" + this.getClass().getPackage().getName();
+        }
+    }
+
+    /**
+     * Filter testing Grizzly request/response injection support into singleton providers.
+     */
+    @PreMatching
+    public static class GrizzlyRequestFilter implements ContainerRequestFilter {
+        @Inject
+        private Provider<org.glassfish.grizzly.http.server.Request> grizzlyRequest;
+        @Inject
+        private org.glassfish.grizzly.http.server.Response grizzlyResponse;
+
+        @Override
+        public void filter(ContainerRequestContext ctx) throws IOException {
+            StringBuilder sb = new StringBuilder();
+
+            // First, make sure there are no null injections.
+            if (grizzlyRequest == null) {
+                sb.append("Grizzly Request is null.\n");
+            }
+            if (grizzlyResponse == null) {
+                sb.append("Grizzly Response is null.\n");
+            }
+
+            if (sb.length() > 0) {
+                ctx.abortWith(Response.serverError().entity(sb.toString()).build());
+            }
+
+            // let's also test some method calls
+            int flags = 0;
+
+            if ("/jersey-2462".equals(grizzlyRequest.get().getContextPath())) {
+                flags += 1;
+            }
+            if (!grizzlyResponse.isCommitted()) {
+                flags += 10;
+            }
+            final String header = grizzlyRequest.get().getHeader(REQUEST_NUMBER);
+
+            ctx.setEntityStream(new ByteArrayInputStream(("filtered-" + flags + "-" + header).getBytes()));
+        }
+    }
+
+    public static class GrizzlyContainerTest extends JerseyTest {
+        @Override
+        protected DeploymentContext configureDeployment() {
+            return DeploymentContext.builder(new ResourceConfig(EchoResource.class, GrizzlyRequestFilter.class))
+                    .contextPath("jersey-2462")
+                    .build();
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new GrizzlyTestContainerFactory();
+        }
+
+        /**
+         * Reproducer for JERSEY-2462 on Grizzly container.
+         */
+        @Test
+        public void testReqestReponseInjectionIntoSingletonProvider() {
+            Jersey2462Test.testReqestReponseInjectionIntoSingletonProvider(target());
+        }
+    }
+
+    /**
+     * Filter testing Jetty request/response injection support into singleton providers.
+     */
+    @PreMatching
+    public static class JettyRequestFilter implements ContainerRequestFilter {
+        @Inject
+        private Provider<org.eclipse.jetty.server.Request> jettyRequest;
+        @Inject
+        private Provider<org.eclipse.jetty.server.Response> jettyResponse;
+
+        @Override
+        public void filter(ContainerRequestContext ctx) throws IOException {
+            StringBuilder sb = new StringBuilder();
+
+            // First, make sure there are no null injections.
+            if (jettyRequest == null) {
+                sb.append("Jetty Request is null.\n");
+            }
+            if (jettyResponse == null) {
+                sb.append("Jetty Response is null.\n");
+            }
+
+            if (sb.length() > 0) {
+                ctx.abortWith(Response.serverError().entity(sb.toString()).build());
+            }
+
+            // let's also test some method calls
+            int flags = 0;
+
+            if ("/echo".equals(jettyRequest.get().getPathInfo())) {
+                flags += 1;
+            }
+            if (!jettyResponse.get().isCommitted()) {
+                flags += 10;
+            }
+            final String header = jettyRequest.get().getHeader(REQUEST_NUMBER);
+
+            ctx.setEntityStream(new ByteArrayInputStream(("filtered-" + flags + "-" + header).getBytes()));
+        }
+    }
+
+
+    public static class JettyContainerTest extends JerseyTest {
+        @Override
+        protected DeploymentContext configureDeployment() {
+            return DeploymentContext.builder(new ResourceConfig(EchoResource.class, JettyRequestFilter.class))
+                    .build();
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new JettyTestContainerFactory();
+        }
+
+        /**
+         * Reproducer for JERSEY-2462 on Grizzly container.
+         */
+        @Test
+        public void testReqestReponseInjectionIntoSingletonProvider() {
+            Jersey2462Test.testReqestReponseInjectionIntoSingletonProvider(target());
+        }
+    }
+
+    /**
+     * Filter testing Simple framework request/response injection support into singleton providers.
+     */
+    @PreMatching
+    public static class SimpleRequestFilter implements ContainerRequestFilter {
+        @Inject
+        private org.simpleframework.http.Request simpleRequest;
+        @Inject
+        private org.simpleframework.http.Response simpleResponse;
+
+        @Override
+        public void filter(ContainerRequestContext ctx) throws IOException {
+            StringBuilder sb = new StringBuilder();
+
+            // First, make sure there are no null injections.
+            if (simpleRequest == null) {
+                sb.append("Simple HTTP framework Request is null.\n");
+            }
+            if (simpleResponse == null) {
+                sb.append("Simple HTTP framework Response is null.\n");
+            }
+
+            if (sb.length() > 0) {
+                ctx.abortWith(Response.serverError().entity(sb.toString()).build());
+            }
+
+            // let's also test some method calls
+            int flags = 0;
+
+            if ("/echo".equals(simpleRequest.getAddress().getPath().getPath())) {
+                flags += 1;
+            }
+            if (!simpleResponse.isCommitted()) {
+                flags += 10;
+            }
+            final String header = simpleRequest.getValue(REQUEST_NUMBER);
+
+            ctx.setEntityStream(new ByteArrayInputStream(("filtered-" + flags + "-" + header).getBytes()));
+        }
+    }
+
+    public static class SimpleContainerTest extends JerseyTest {
+        @Override
+        protected DeploymentContext configureDeployment() {
+            return DeploymentContext.builder(new ResourceConfig(EchoResource.class, SimpleRequestFilter.class))
+                    .build();
+        }
+
+        @Override
+        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+            return new SimpleTestContainerFactory();
+        }
+
+        /**
+         * Reproducer for JERSEY-2462 on Grizzly container.
+         */
+        @Test
+        public void testReqestReponseInjectionIntoSingletonProvider() {
+            Jersey2462Test.testReqestReponseInjectionIntoSingletonProvider(target());
+        }
+    }
+
+    /**
+     * Reproducer method for JERSEY-2462.
+     */
+    public static void testReqestReponseInjectionIntoSingletonProvider(WebTarget target) {
+        for (int i = 0; i < 10; i++) {
+            String response = target.path("echo").request().header(REQUEST_NUMBER, i)
+                    .post(Entity.text("test"), String.class);
+            // Assert that the request has been filtered and processed by the echo method.
+            assertEquals(new EchoResource().echo("filtered-11-" + i), response);
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
new file mode 100644
index 0000000..fe2b087
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JerseyContainerTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.netty.NettyTestContainerFactory;
+import org.glassfish.jersey.test.simple.SimpleTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(Parameterized.class)
+public abstract class JerseyContainerTest extends JerseyTest {
+
+    private static final List<TestContainerFactory> FACTORIES = Arrays.asList(
+            new GrizzlyTestContainerFactory(),
+            new InMemoryTestContainerFactory(),
+            new SimpleTestContainerFactory(),
+            new JdkHttpServerTestContainerFactory(),
+            new JettyTestContainerFactory(),
+            new NettyTestContainerFactory()
+    );
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<TestContainerFactory[]> parameters() throws Exception {
+        return FACTORIES.stream().map(input -> new TestContainerFactory[]{input}).collect(Collectors.toList());
+    }
+
+
+    @Parameterized.Parameter(0)
+    public TestContainerFactory factory;
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return factory;
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java
new file mode 100644
index 0000000..4af03c6
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/JettyEmptyHeaderParamTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 2018 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.container;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests Jetty getHeader() - returns null in when the header
+ * is not present and even though the header is empty string.
+ *
+ * ISSUE: JERSEY-2917
+ *
+ * @author Petr Bouda
+ * */
+public class JettyEmptyHeaderParamTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HeaderResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new JettyTestContainerFactory();
+    }
+
+    @Path("test")
+    public static class HeaderResource {
+
+        @GET
+        public String get(@HeaderParam("a") String value) {
+            return value;
+        }
+
+    }
+
+    @Test
+    public void testNullHeader() throws Exception {
+        String response = target("test").request().get(String.class);
+        assertEquals("", response);
+    }
+
+    @Test
+    public void testEmptyHeader() throws Exception {
+        String response = target("test").request().header("a", "").get(String.class);
+        assertEquals("", response);
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java
new file mode 100644
index 0000000..f9b3bd8
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/LeadingSlashesTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2015, 2018 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.container;
+
+import java.net.URI;
+
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertNotEquals;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Test Jersey container implementation of URL resolving.
+ * In this test there is no context path that means that
+ * slashes in URL are part of Resource address and couldn't
+ * be deleted.
+ *
+ * @author Petr Bouda
+ */
+public class LeadingSlashesTest extends JerseyContainerTest {
+
+    public static final String CONTAINER_RESPONSE = "Container-Response";
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(SimpleResource.class,
+                EmptyResource.class,
+                EmptyPathParamResource.class);
+
+        resourceConfig.property(ServerProperties.REDUCE_CONTEXT_PATH_SLASHES_ENABLED, true);
+        return resourceConfig;
+    }
+
+    @Path("simple")
+    public static class SimpleResource {
+
+        @GET
+        public String encoded() {
+            return CONTAINER_RESPONSE;
+        }
+
+    }
+
+    @Path("/")
+    public static class EmptyPathParamResource {
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/test")
+        public String getHello(@PathParam("bar") final String bar, @PathParam("baz") final String baz) {
+            return bar + "-" + baz;
+        }
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/testParams")
+        public String helloWithQueryParams(@PathParam("bar") final String bar, @PathParam("baz") final String baz,
+                                           @QueryParam("bar") final String queryBar, @QueryParam("baz") final String queryBaz) {
+            return "PATH PARAM: " + bar + "-" + baz + ", QUERY PARAM " + queryBar + "-" + queryBaz;
+        }
+
+        @GET
+        @Path("{bar:.*}/{baz:.*}/encoded")
+        public String getEncoded(@Encoded @QueryParam("query") String queryParam) {
+            return queryParam.equals("%25dummy23%2Ba") + ":" + queryParam;
+        }
+    }
+
+    @Path("/")
+    public static class EmptyResource {
+
+        @GET
+        @Path("/test")
+        public String getHello() {
+            return CONTAINER_RESPONSE;
+        }
+    }
+
+    @Test
+    public void testSimpleSlashes() {
+        Response result = call("/simple");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+
+        result = call("//simple");
+        assertNotEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmpty() {
+        Response result = call("/test");
+        assertEquals(CONTAINER_RESPONSE, result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmptyPathParam() {
+        Response result = call("///test");
+        assertEquals("-", result.readEntity(String.class));
+    }
+
+    @Test
+    public void testSlashesWithBeginningEmptyPathParamWithQueryParams() {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        WebTarget target = client().target(hostPort).path("///testParams")
+                .queryParam("bar", "Container")
+                .queryParam("baz", "Response");
+
+        Response result = target.request().get();
+        assertEquals("PATH PARAM: -, QUERY PARAM Container-Response", result.readEntity(String.class));
+    }
+
+    @Test
+    public void testEncodedQueryParams() {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        WebTarget target = client().target(hostPort).path("///encoded")
+                .queryParam("query", "%dummy23+a");
+
+        Response response = target.request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("true:%25dummy23%2Ba", response.readEntity(String.class));
+    }
+
+
+    private Response call(String path) {
+        URI hostPort = UriBuilder.fromUri("http://localhost/").port(getPort()).build();
+        return client().target(hostPort).path(path).request().get();
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/MatrixParamTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/MatrixParamTest.java
new file mode 100644
index 0000000..ed96f47
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/MatrixParamTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MatrixParamTest extends JerseyContainerTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String get(@MatrixParam("x") final String x, @MatrixParam("y") final String y) {
+            return y;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testMatrixParam() {
+        assertThat(target().matrixParam("y", "1").request().get(String.class), is("1"));
+        assertThat(target().matrixParam("x", "1").matrixParam("y", "1%20%2B%202").request().get(String.class), is("1 + 2"));
+        assertThat(target().matrixParam("x", "1").matrixParam("y", "1%20%26%202").request().get(String.class), is("1 & 2"));
+        assertThat(target().matrixParam("x", "1").matrixParam("y", "1%20%7C%7C%202").request().get(String.class), is("1 || 2"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/QueryParamTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/QueryParamTest.java
new file mode 100644
index 0000000..7e95477
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/QueryParamTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class QueryParamTest extends JerseyContainerTest {
+
+    @Path("/")
+    public static class Resource {
+        @GET
+        public String get(@QueryParam("x") final String x, @QueryParam("y") final String y) {
+            return y;
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testQueryParam() {
+        assertThat(target().queryParam("y", "1 %2B 2").request().get(String.class), is("1 + 2"));
+        assertThat(target().queryParam("x", "1").queryParam("y", "1 + 2").request().get(String.class), is("1 + 2"));
+        assertThat(target().queryParam("x", "1").queryParam("y", "1 %26 2").request().get(String.class), is("1 & 2"));
+        assertThat(target().queryParam("x", "1").queryParam("y", "1 %7C%7C 2").request().get(String.class), is("1 || 2"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResponseWriterMetadataTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResponseWriterMetadataTest.java
new file mode 100644
index 0000000..3035489
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResponseWriterMetadataTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ResponseWriterMetadataTest extends JerseyContainerTest {
+
+    public static class ValueHolder {
+
+        private String value;
+
+        public ValueHolder(final String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(final String value) {
+            this.value = value;
+        }
+    }
+
+    @Provider
+    @Produces("text/plain")
+    public static class ValueHolderWriter implements MessageBodyWriter<ValueHolder> {
+
+        public boolean isWriteable(final Class<?> c, final Type t, final Annotation[] as, final MediaType mediaType) {
+            return ValueHolder.class == c;
+        }
+
+        public long getSize(final ValueHolder s, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType) {
+            return -1;
+        }
+
+        public void writeTo(final ValueHolder s,
+                            final Class<?> c,
+                            final Type t,
+                            final Annotation[] as,
+                            final MediaType mt,
+                            final MultivaluedMap<String, Object> headers,
+                            final OutputStream out) throws IOException, WebApplicationException {
+
+            headers.add("X-BEFORE-WRITE", "foo");
+            out.write(s.value.getBytes());
+            headers.add("X-AFTER-WRITE", "bar");
+        }
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public ValueHolder get() {
+            return new ValueHolder("one");
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class, ValueHolderWriter.class)
+                .property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 1);
+    }
+
+    @Test
+    public void testResponse() {
+        final Response response = target().request().get();
+
+        assertThat(response.readEntity(String.class), is("one"));
+        assertThat(response.getHeaderString("X-BEFORE-WRITE"), is("foo"));
+
+        if (factory instanceof InMemoryTestContainerFactory) {
+            assertThat(response.getHeaderString("X-AFTER-WRITE"), is("bar"));
+        } else {
+            assertThat(response.getHeaderString("X-AFTER-WRITE"), nullValue());
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResponseWriterOutputStreamTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResponseWriterOutputStreamTest.java
new file mode 100644
index 0000000..7edcbc4
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResponseWriterOutputStreamTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This is really weird approach and test.
+ *
+ * @author Michal Gajdos
+ */
+public class ResponseWriterOutputStreamTest extends JerseyContainerTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        @Produces("text/plain")
+        public void get(final ContainerRequest context) throws IOException {
+            assertThat(context.getMethod(), is("GET"));
+
+            final ContainerResponse response = new ContainerResponse(context, Response.ok().build());
+            final OutputStream os = context.getResponseWriter()
+                    .writeResponseStatusAndHeaders("RESOURCE".getBytes().length, response);
+            os.write("RESOURCE".getBytes());
+            os.close();
+        }
+
+        @POST
+        @Produces("text/plain")
+        public void post(final ContainerRequest context) throws IOException {
+            assertThat(context.getMethod(), is("POST"));
+
+            final String s = context.readEntity(String.class);
+            assertEquals("RESOURCE", s);
+
+            final ContainerResponse response = new ContainerResponse(context, Response.ok().build());
+            final OutputStream os = context.getResponseWriter()
+                    .writeResponseStatusAndHeaders("RESOURCE".getBytes().length, response);
+            os.write("RESOURCE".getBytes());
+            os.close();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testGet() {
+        assertThat(target().request().get(String.class), is("RESOURCE"));
+    }
+
+    @Test
+    public void testPost() {
+        assertThat(target().request().post(Entity.text("RESOURCE"), String.class), is("RESOURCE"));
+    }
+
+    @Test
+    public void testAll() {
+        testGet();
+        testPost();
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/TrailingSlashTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/TrailingSlashTest.java
new file mode 100644
index 0000000..b55cb38
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/TrailingSlashTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014, 2018 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.container;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class TrailingSlashTest extends JerseyContainerTest {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        @Path("get")
+        public String get() {
+            return "get";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testSlash() throws Exception {
+        _test("get/");
+    }
+
+    @Test
+    public void testNoSlash() throws Exception {
+        _test("get");
+    }
+
+    private void _test(final String path) {
+        final Response response = target(path).request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("get"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/AccessTokenResource.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/AccessTokenResource.java
new file mode 100644
index 0000000..be10824
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/AccessTokenResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2018 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.oauth;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.oauth1.signature.OAuth1Parameters;
+import org.glassfish.jersey.oauth1.signature.OAuth1Secrets;
+import org.glassfish.jersey.oauth1.signature.OAuth1Signature;
+import org.glassfish.jersey.oauth1.signature.OAuth1SignatureException;
+import org.glassfish.jersey.server.oauth1.internal.OAuthServerRequest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@Path("/access_token")
+public class AccessTokenResource {
+
+    @Inject
+    private OAuth1Signature oAuth1Signature;
+
+    @POST
+    @Produces("text/plain")
+    public String post(@Context ContainerRequestContext request) {
+
+        OAuthServerRequest osr = new OAuthServerRequest(request);
+
+        OAuth1Secrets secrets = new OAuth1Secrets()
+                .consumerSecret("kd94hf93k423kf44").tokenSecret("hdhd0244k9j7ao03");
+
+        OAuth1Parameters params = new OAuth1Parameters().readRequest(osr);
+
+        // ensure query parameters correctly parsed into OAuth parameters object
+        assertEquals(params.getConsumerKey(), "dpf43f3p2l4k3l03");
+        assertEquals(params.getToken(), "hh5s93j4hdidpola");
+        assertEquals(params.getSignatureMethod(), "PLAINTEXT");
+        assertEquals(params.getSignature(), secrets.getConsumerSecret() + "&" + secrets.getTokenSecret());
+        assertEquals(params.getTimestamp(), "1191242092");
+        assertEquals(params.getNonce(), "dji430splmx33448");
+        assertEquals(params.getVersion(), "1.0");
+
+        try {
+            // verify the plaintext signature
+            assertTrue(oAuth1Signature.verify(osr, params, secrets));
+        } catch (OAuth1SignatureException ose) {
+            fail(ose.getMessage());
+        }
+
+        return "oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00";
+    }
+
+}
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OAuth2Test.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OAuth2Test.java
new file mode 100644
index 0000000..77323c4
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OAuth2Test.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2013, 2018 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.oauth;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.oauth2.ClientIdentifier;
+import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
+import org.glassfish.jersey.client.oauth2.OAuth2CodeGrantFlow;
+import org.glassfish.jersey.client.oauth2.OAuth2Parameters;
+import org.glassfish.jersey.client.oauth2.TokenResult;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests OAuth 2 client.
+ *
+ * @author Miroslav Fuksa
+ */
+public class OAuth2Test extends JerseyTest {
+
+    private static final String STATE = "4564dsf54654fsda654af";
+    private static final String CODE = "code-xyz";
+    private static final String CLIENT_PUBLIC = "clientPublic";
+    private static final String CLIENT_SECRET = "clientSecret";
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(MoxyJsonFeature.class, AuthorizationResource.class)
+                .register(new LoggingFeature(Logger.getAnonymousLogger(), LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    @Path("oauth")
+    public static class AuthorizationResource {
+
+        @POST
+        @Path("access-token")
+        @Produces(MediaType.APPLICATION_JSON)
+        public MyTokenResult getAccessToken(@FormParam("grant_type") String grantType,
+                                            @FormParam("code") String code,
+                                            @FormParam("redirect_uri") String redirectUri,
+                                            @FormParam("client_id") String clientId) {
+            try {
+                assertEquals("authorization_code", grantType);
+                assertEquals("urn:ietf:wg:oauth:2.0:oob", redirectUri);
+                assertEquals(CODE, code);
+                assertEquals(CLIENT_PUBLIC, clientId);
+            } catch (AssertionError e) {
+                e.printStackTrace();
+                throw new BadRequestException(Response.status(400).entity(e.getMessage()).build());
+            }
+
+            final MyTokenResult myTokenResult = new MyTokenResult();
+            myTokenResult.setAccessToken("access-token-aab999f");
+            myTokenResult.setExpiresIn("3600");
+            myTokenResult.setTokenType("access-token");
+            myTokenResult.setRefreshToken("refresh-xyz");
+            return myTokenResult;
+        }
+
+        @GET
+        @Path("authorization")
+        public String authorization(@QueryParam("state") String state,
+                                    @QueryParam("response_type") String responseType,
+                                    @QueryParam("scope") String scope,
+                                    @QueryParam("readOnly") String readOnly,
+                                    @QueryParam("redirect_uri") String redirectUri) {
+            try {
+                assertEquals("code", responseType);
+                assertEquals(STATE, state);
+                assertEquals("urn:ietf:wg:oauth:2.0:oob", redirectUri);
+                assertEquals("contact", scope);
+                assertEquals("true", readOnly);
+            } catch (AssertionError e) {
+                e.printStackTrace();
+                throw new BadRequestException(Response.status(400).entity(e.getMessage()).build());
+            }
+            return CODE;
+        }
+
+        @POST
+        @Path("refresh-token")
+        @Produces(MediaType.APPLICATION_JSON)
+        public String refreshToken(@FormParam("grant_type") String grantType,
+                                   @FormParam("refresh_token") String refreshToken,
+                                   @HeaderParam("isArray") @DefaultValue("false") boolean isArray) {
+            try {
+                assertEquals("refresh_token", grantType);
+                assertEquals("refresh-xyz", refreshToken);
+            } catch (AssertionError e) {
+                e.printStackTrace();
+                throw new BadRequestException(Response.status(400).entity(e.getMessage()).build());
+            }
+
+            return isArray
+                    ? "{\"access_token\":[\"access-token-new\"],\"expires_in\":\"3600\",\"token_type\":\"access-token\"}"
+                    : "{\"access_token\":\"access-token-new\",\"expires_in\":\"3600\",\"token_type\":\"access-token\"}";
+        }
+    }
+
+    @Test
+    public void testFlow() {
+        testFlow(false);
+    }
+
+    @Test
+    public void testFlowWithArrayInResponse() {
+        testFlow(true);
+    }
+
+    private void testFlow(final boolean isArray) {
+        ClientIdentifier clientId = new ClientIdentifier(CLIENT_PUBLIC, CLIENT_SECRET);
+        final String authUri = UriBuilder.fromUri(getBaseUri()).path("oauth").path("authorization").build().toString();
+        final String accessTokenUri = UriBuilder.fromUri(getBaseUri()).path("oauth").path("access-token").build().toString();
+        final String refreshTokenUri = UriBuilder.fromUri(getBaseUri()).path("oauth").path("refresh-token").build().toString();
+        final String state = STATE;
+
+        final Client client = ClientBuilder.newClient();
+        if (isArray) {
+            client.register(new ClientRequestFilter() {
+                @Override
+                public void filter(final ClientRequestContext requestContext) throws IOException {
+                    requestContext.getHeaders().putSingle("isArray", true);
+                }
+            });
+        }
+
+        final OAuth2CodeGrantFlow.Builder builder =
+                OAuth2ClientSupport.authorizationCodeGrantFlowBuilder(clientId, authUri, accessTokenUri);
+        final OAuth2CodeGrantFlow flow = builder
+                .client(client)
+                .refreshTokenUri(refreshTokenUri)
+                .property(OAuth2CodeGrantFlow.Phase.AUTHORIZATION, "readOnly", "true")
+                .property(OAuth2CodeGrantFlow.Phase.AUTHORIZATION, OAuth2Parameters.STATE, state)
+                .scope("contact")
+                .build();
+        final String finalAuthorizationUri = flow.start();
+
+        final Response response = ClientBuilder.newClient().target(finalAuthorizationUri).request().get();
+        assertEquals(200, response.getStatus());
+
+        final String code = response.readEntity(String.class);
+        assertEquals(CODE, code);
+
+        final TokenResult result = flow.finish(code, state);
+        assertEquals("access-token-aab999f", result.getAccessToken());
+        assertEquals(new Long(3600), result.getExpiresIn());
+        assertEquals("access-token", result.getTokenType());
+
+        final TokenResult refreshResult = flow.refreshAccessToken(result.getRefreshToken());
+        assertEquals("access-token-new", refreshResult.getAccessToken());
+        assertEquals(new Long(3600), refreshResult.getExpiresIn());
+        assertEquals("access-token", refreshResult.getTokenType());
+
+        if (isArray) {
+            final Collection<String> array = (Collection<String>) refreshResult.getAllProperties().get("access_token");
+
+            assertThat(array.size(), is(1));
+            assertThat(array, hasItem("access-token-new"));
+        }
+    }
+
+    @XmlRootElement
+    public static class MyTokenResult {
+
+        @XmlAttribute(name = "access_token")
+        private String accessToken;
+        @XmlAttribute(name = "expires_in")
+        private String expiresIn;
+        @XmlAttribute(name = "token_type")
+        private String tokenType;
+
+        public String getRefreshToken() {
+            return refreshToken;
+        }
+
+        public void setRefreshToken(String refreshToken) {
+            this.refreshToken = refreshToken;
+        }
+
+        @XmlAttribute(name = "refresh_token")
+        private String refreshToken;
+
+        public String getAccessToken() {
+            return accessToken;
+        }
+
+        public void setAccessToken(String accessToken) {
+            this.accessToken = accessToken;
+        }
+
+        public String getExpiresIn() {
+            return expiresIn;
+        }
+
+        public void setExpiresIn(String expiresIn) {
+            this.expiresIn = expiresIn;
+        }
+
+        public String getTokenType() {
+            return tokenType;
+        }
+
+        public void setTokenType(String tokenType) {
+            this.tokenType = tokenType;
+        }
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OAuthClientServerTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OAuthClientServerTest.java
new file mode 100644
index 0000000..533e724
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OAuthClientServerTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2013, 2018 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.oauth;
+
+import java.net.URI;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.client.oauth1.AccessToken;
+import org.glassfish.jersey.client.oauth1.ConsumerCredentials;
+import org.glassfish.jersey.client.oauth1.OAuth1AuthorizationFlow;
+import org.glassfish.jersey.client.oauth1.OAuth1ClientSupport;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.oauth1.DefaultOAuth1Provider;
+import org.glassfish.jersey.server.oauth1.OAuth1Provider;
+import org.glassfish.jersey.server.oauth1.OAuth1ServerFeature;
+import org.glassfish.jersey.server.oauth1.OAuth1ServerProperties;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import com.sun.security.auth.UserPrincipal;
+
+/**
+ * Tests client and server OAuth 1 functionality.
+ *
+ * @author Miroslav Fuksa
+ */
+public class OAuthClientServerTest extends JerseyTest {
+
+    private static final String SECRET_CONSUMER_KEY = "secret-consumer-key";
+    private static final String CONSUMER_KEY = "my-consumer-key";
+    private static final String CONSUMER_NAME = "my-consumer";
+    private static final String PROMETHEUS_TOKEN = "prometheus-token";
+    private static final String PROMETHEUS_SECRET = "prometheus-secret";
+
+    @Override
+    protected Application configure() {
+
+        final DefaultOAuth1Provider oAuthProvider = new DefaultOAuth1Provider();
+        oAuthProvider.registerConsumer(CONSUMER_NAME, CONSUMER_KEY,
+                SECRET_CONSUMER_KEY, new MultivaluedHashMap<String, String>());
+
+        final Principal prometheusPrincipal = new Principal() {
+            @Override
+            public String getName() {
+                return "prometheus";
+            }
+        };
+
+        oAuthProvider.addAccessToken(PROMETHEUS_TOKEN, PROMETHEUS_SECRET, CONSUMER_KEY,
+                "http://callback.url", prometheusPrincipal,
+                Arrays.asList("admin", "user").stream().collect(Collectors.toSet()),
+                new MultivaluedHashMap<String, String>());
+        final OAuth1ServerFeature oAuth1ServerFeature = new OAuth1ServerFeature(oAuthProvider,
+                "requestTokenSpecialUri", "accessTokenSpecialUri");
+        final ResourceConfig resourceConfig = new ResourceConfig();
+        resourceConfig.register(oAuth1ServerFeature);
+        resourceConfig.register(MyProtectedResource.class);
+        resourceConfig.register(new LoggingFeature(Logger.getLogger(OAuthClientServerTest.class.getName()),
+                LoggingFeature.Verbosity.PAYLOAD_ANY));
+        resourceConfig.register(OAuthAuthorizationResource.class);
+        resourceConfig.property(OAuth1ServerProperties.TIMESTAMP_UNIT, "SECONDS");
+        resourceConfig.property(OAuth1ServerProperties.MAX_NONCE_CACHE_SIZE, 20);
+        return resourceConfig;
+    }
+
+    @Path("resource")
+    public static class MyProtectedResource {
+
+        @Context
+        private SecurityContext securityContext;
+
+        @GET
+        public String get() {
+            return securityContext.getUserPrincipal().getName();
+        }
+
+        @Path("admin")
+        @GET
+        public boolean getFoo() {
+            return securityContext.isUserInRole("admin");
+        }
+    }
+
+    @Path("user-authorization")
+    public static class OAuthAuthorizationResource {
+
+        @Inject
+        private OAuth1Provider provider;
+
+        @GET
+        public String mustBeGetMethod(@QueryParam("oauth_token") String token) {
+            System.out.println("Token received from user: " + token);
+            final DefaultOAuth1Provider defProvider = (DefaultOAuth1Provider) provider;
+            assertEquals("http://consumer/callback/homer", defProvider.getRequestToken(token).getCallbackUrl());
+
+            return defProvider.authorizeToken(
+                    defProvider.getRequestToken(token),
+                    new UserPrincipal("homer"),
+                    Collections.singleton("user"));
+        }
+    }
+
+    /**
+     * Tests client and server OAuth.
+     * <p/>
+     * Tests authorization flow including the request to a protected resource. The test uses {@link OAuth1AuthorizationFlow}
+     * to perform user authorization and uses authorized client for requesting protected resource.
+     * <p/>
+     * The resource {@link OAuthAuthorizationResource} is used to perform user authorization (this is done
+     * programmatically from the test). Finally, the Access Token is retrieved and used to request the
+     * protected resource. In this resource the user principal is used to return the name of the user stored
+     * in {@link SecurityContext}.
+     */
+    @Test
+    public void testAuthorizationFlow() {
+        String tempCredUri = UriBuilder.fromUri(getBaseUri()).path("requestTokenSpecialUri").build().toString();
+        String accessTokenUri = UriBuilder.fromUri(getBaseUri()).path("accessTokenSpecialUri").build().toString();
+        final String userAuthorizationUri = UriBuilder.fromUri(getBaseUri()).path("user-authorization").build().toString();
+        final OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport
+                .builder(new ConsumerCredentials(CONSUMER_KEY, SECRET_CONSUMER_KEY))
+                .authorizationFlow(tempCredUri, accessTokenUri, userAuthorizationUri)
+                .callbackUri("http://consumer/callback/homer").build();
+
+        final String authUri = authFlow.start();
+        // authorize by a request to authorization URI
+        final Response userAuthResponse = ClientBuilder.newClient().target(authUri).request().get();
+        assertEquals(200, userAuthResponse.getStatus());
+        final String verifier = userAuthResponse.readEntity(String.class);
+        System.out.println("Verifier: " + verifier);
+
+        authFlow.finish(verifier);
+        final Client authorizedClient = authFlow.getAuthorizedClient();
+
+        Response response = authorizedClient.target(getBaseUri()).path("resource")
+                .request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("homer", response.readEntity(String.class));
+
+        response = authorizedClient.target(getBaseUri()).path("resource").path("admin").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals(false, response.readEntity(boolean.class));
+    }
+
+    /**
+     * Tests {@link org.glassfish.jersey.client.oauth1.OAuth1ClientFilter} already configured with Access Token for signature
+     * purposes only.
+     */
+    @Test
+    public void testRequestSigning() {
+        final Feature filterFeature = OAuth1ClientSupport.builder(
+                new ConsumerCredentials(CONSUMER_KEY, SECRET_CONSUMER_KEY)).feature()
+                .accessToken(new AccessToken(PROMETHEUS_TOKEN, PROMETHEUS_SECRET)).build();
+        final Client client = ClientBuilder.newBuilder()
+                .register(filterFeature).build();
+        final URI resourceUri = UriBuilder.fromUri(getBaseUri()).path("resource").build();
+        final WebTarget target = client.target(resourceUri);
+        Response response;
+        for (int i = 0; i < 15; i++) {
+            System.out.println("request: " + i);
+            response = target.request().get();
+            assertEquals(200, response.getStatus());
+            assertEquals("prometheus", response.readEntity(String.class));
+            i++;
+            response = target.path("admin").request().get();
+            assertEquals(200, response.getStatus());
+            assertEquals(true, response.readEntity(boolean.class));
+        }
+    }
+
+    /**
+     * Tests configuration of the nonce cache on the server side.
+     */
+    @Test
+    public void testRequestSigningWithExceedingCache() {
+        final Feature filterFeature = OAuth1ClientSupport.builder(
+                new ConsumerCredentials(CONSUMER_KEY, SECRET_CONSUMER_KEY)).feature()
+                .accessToken(new AccessToken(PROMETHEUS_TOKEN, PROMETHEUS_SECRET)).build();
+        final Client client = ClientBuilder.newBuilder()
+                .register(filterFeature).build();
+        final URI resourceUri = UriBuilder.fromUri(getBaseUri()).path("resource").build();
+        final WebTarget target = client.target(resourceUri);
+        Response response;
+        for (int i = 0; i < 20; i++) {
+            System.out.println("request: " + i);
+            response = target.request().get();
+            assertEquals(200, response.getStatus());
+            assertEquals("prometheus", response.readEntity(String.class));
+            i++;
+            response = target.path("admin").request().get();
+            assertEquals(200, response.getStatus());
+            assertEquals(true, response.readEntity(boolean.class));
+        }
+        // now the nonce cache is full
+        response = target.request().get();
+        assertEquals(401, response.getStatus());
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OauthClientAuthorizationFlowTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OauthClientAuthorizationFlowTest.java
new file mode 100644
index 0000000..147772e
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/OauthClientAuthorizationFlowTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2014, 2018 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.oauth;
+
+import java.lang.reflect.Field;
+import java.net.URI;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.client.oauth1.AccessToken;
+import org.glassfish.jersey.client.oauth1.ConsumerCredentials;
+import org.glassfish.jersey.client.oauth1.OAuth1AuthorizationFlow;
+import org.glassfish.jersey.client.oauth1.OAuth1Builder;
+import org.glassfish.jersey.client.oauth1.OAuth1ClientSupport;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.oauth1.signature.OAuth1Parameters;
+import org.glassfish.jersey.oauth1.signature.OAuth1SignatureFeature;
+import org.glassfish.jersey.oauth1.signature.PlaintextMethod;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class OauthClientAuthorizationFlowTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(AccessTokenResource.class, PhotosResource.class, RequestTokenResource.class,
+                OAuth1SignatureFeature.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new GrizzlyTestContainerFactory();
+    }
+
+    /**
+     * Tests mainly the client functionality. The test client registers
+     * {@link org.glassfish.jersey.client.oauth1.OAuth1ClientFilter} and uses the filter only to sign requests. So, it does not
+     * use the filter to perform authorization flow. However, each request that this test performs is actually a request used
+     * during the authorization flow.
+     * <p/>
+     * The server side of this test extracts header authorization values and tests that signatures are
+     * correct for each request type.
+     */
+    @Test
+    public void testOAuthClientFeature() {
+        final URI baseUri = getBaseUri();
+
+        // baseline for requests
+        final OAuth1Builder oAuth1Builder = OAuth1ClientSupport.builder(new ConsumerCredentials("dpf43f3p2l4k3l03",
+                "kd94hf93k423kf44")).timestamp("1191242090").nonce("hsu94j3884jdopsl").signatureMethod(PlaintextMethod.NAME)
+                .version("1.0");
+        final Feature feature = oAuth1Builder.feature().build();
+
+        final Client client = client();
+        client.register(LoggingFeature.class);
+        final WebTarget target = client.target(baseUri);
+
+        // simulate request for Request Token (temporary credentials)
+        String responseEntity = target.path("request_token").register(feature).request().post(Entity.entity("entity",
+                MediaType.TEXT_PLAIN_TYPE), String.class);
+        assertEquals(responseEntity, "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03");
+
+        final Feature feature2 = oAuth1Builder.timestamp("1191242092").nonce("dji430splmx33448").feature().accessToken(new
+                AccessToken("hh5s93j4hdidpola", "hdhd0244k9j7ao03")).build();
+
+        // simulate request for Access Token
+        responseEntity = target.path("access_token").register(feature2).request().post(Entity.entity("entity",
+                MediaType.TEXT_PLAIN_TYPE), String.class);
+        assertEquals(responseEntity, "oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00");
+
+        final Feature feature3 = oAuth1Builder.nonce("kllo9940pd9333jh").signatureMethod("HMAC-SHA1").timestamp("1191242096")
+                .feature().accessToken(new AccessToken("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00")).build();
+
+        // based on Access Token
+        responseEntity = target.path("/photos").register(feature3).queryParam("file", "vacation.jpg").queryParam("size",
+                "original").request().get(String.class);
+
+        assertEquals(responseEntity, "PHOTO");
+    }
+
+    @Test
+    public void testOAuthClientFlow() throws Exception {
+        final String uri = getBaseUri().toString();
+
+        final OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport
+                .builder(new ConsumerCredentials("dpf43f3p2l4k3l03", "kd94hf93k423kf44"))
+                .timestamp("1191242090")
+                .nonce("hsu94j3884jdopsl")
+                .signatureMethod("PLAINTEXT")
+                .authorizationFlow(
+                        uri + "request_token",
+                        uri + "access_token",
+                        uri + "authorize")
+                .enableLogging()
+                .build();
+
+        // Check we have correct authorization URI.
+        final String authorizationUri = authFlow.start();
+        assertThat(authorizationUri, containsString("authorize?oauth_token=hh5s93j4hdidpola"));
+
+        // For the purpose of the test I need parameters (and there is no way how to do it now).
+        final Field paramField = authFlow.getClass().getDeclaredField("parameters");
+        paramField.setAccessible(true);
+        final OAuth1Parameters params = (OAuth1Parameters) paramField.get(authFlow);
+
+        // Update parameters.
+        params.timestamp("1191242092").nonce("dji430splmx33448");
+
+        final AccessToken accessToken = authFlow.finish();
+        assertThat(accessToken, equalTo(new AccessToken("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00")));
+
+        // Update parameters before creating a feature (i.e. changing signature method).
+        params.nonce("kllo9940pd9333jh").signatureMethod("HMAC-SHA1").timestamp("1191242096");
+
+        // Check Authorized Client.
+        final Client flowClient = authFlow.getAuthorizedClient().register(LoggingFeature.class);
+
+        String responseEntity = flowClient.target(uri).path("/photos")
+                .queryParam("file", "vacation.jpg")
+                .queryParam("size", "original")
+                .request()
+                .get(String.class);
+
+        assertThat("Flow Authorized Client", responseEntity, equalTo("PHOTO"));
+
+        // Check Feature.
+        final Client featureClient = ClientBuilder.newClient()
+                .register(authFlow.getOAuth1Feature()).register(LoggingFeature.class);
+
+        responseEntity = featureClient.target(uri).path("/photos")
+                .queryParam("file", "vacation.jpg")
+                .queryParam("size", "original")
+                .request()
+                .get(String.class);
+
+        assertThat("Feature Client", responseEntity, equalTo("PHOTO"));
+    }
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/PhotosResource.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/PhotosResource.java
new file mode 100644
index 0000000..e9cc2ce
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/PhotosResource.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2013, 2018 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.oauth;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.oauth1.signature.OAuth1Parameters;
+import org.glassfish.jersey.oauth1.signature.OAuth1Secrets;
+import org.glassfish.jersey.oauth1.signature.OAuth1Signature;
+import org.glassfish.jersey.oauth1.signature.OAuth1SignatureException;
+import org.glassfish.jersey.server.oauth1.internal.OAuthServerRequest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@Path("/photos")
+public class PhotosResource {
+
+    @Inject
+    private OAuth1Signature oAuth1Signature;
+
+    @GET
+    @Produces("text/plain")
+    public String handle(
+            @QueryParam("file") String file,
+            @QueryParam("size") String size,
+            @Context ContainerRequestContext request) {
+
+        OAuthServerRequest osr = new OAuthServerRequest(request);
+
+        OAuth1Secrets secrets = new OAuth1Secrets()
+                .consumerSecret("kd94hf93k423kf44").tokenSecret("pfkkdhi9sl3r4s00");
+
+        OAuth1Parameters params = new OAuth1Parameters().readRequest(osr);
+
+        // ensure query parameters are as expected
+        assertEquals(file, "vacation.jpg");
+        assertEquals(size, "original");
+
+        // ensure query parameters correctly parsed into OAuth parameters object
+        assertEquals(params.getConsumerKey(), "dpf43f3p2l4k3l03");
+        assertEquals(params.getToken(), "nnch734d00sl2jdk");
+        assertEquals(params.getSignatureMethod(), "HMAC-SHA1");
+        assertEquals(params.getTimestamp(), "1191242096");
+        assertEquals(params.getNonce(), "kllo9940pd9333jh");
+        assertEquals(params.getVersion(), "1.0");
+
+        try {
+            // verify the HMAC-SHA1 signature
+            assertTrue(oAuth1Signature.verify(osr, params, secrets));
+        } catch (OAuth1SignatureException ose) {
+            fail(ose.getMessage());
+        }
+
+        return "PHOTO";
+    }
+}
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/RequestTokenResource.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/RequestTokenResource.java
new file mode 100644
index 0000000..7455e62
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/oauth/RequestTokenResource.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013, 2018 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.oauth;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.oauth1.signature.OAuth1Parameters;
+import org.glassfish.jersey.oauth1.signature.OAuth1Secrets;
+import org.glassfish.jersey.oauth1.signature.OAuth1Signature;
+import org.glassfish.jersey.oauth1.signature.OAuth1SignatureException;
+import org.glassfish.jersey.server.oauth1.internal.OAuthServerRequest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@Path("/request_token")
+public class RequestTokenResource {
+
+    @Inject
+    private OAuth1Signature oAuth1Signature;
+
+    @POST
+    @Produces("text/plain")
+    public String get(@Context ContainerRequestContext request) {
+
+        OAuthServerRequest osr = new OAuthServerRequest(request);
+
+        OAuth1Secrets secrets = new OAuth1Secrets().consumerSecret("kd94hf93k423kf44");
+
+        OAuth1Parameters params = new OAuth1Parameters().readRequest(osr);
+
+        // ensure parameters correctly parsed into OAuth parameters object
+        assertEquals(params.getConsumerKey(), "dpf43f3p2l4k3l03");
+        assertEquals(params.getSignatureMethod(), "PLAINTEXT");
+        assertEquals(params.getSignature(), secrets.getConsumerSecret() + "&");
+        assertEquals(params.getTimestamp(), "1191242090");
+        assertEquals(params.getNonce(), "hsu94j3884jdopsl");
+        assertEquals(params.getVersion(), "1.0");
+
+        try {
+            // verify the plaintext signature
+            assertTrue(oAuth1Signature.verify(osr, params, secrets));
+        } catch (OAuth1SignatureException ose) {
+            fail(ose.getMessage());
+        }
+
+        return "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03";
+    }
+
+}
+
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/sonar/SonarJerseyE2ETest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/sonar/SonarJerseyE2ETest.java
new file mode 100644
index 0000000..a31248b
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/sonar/SonarJerseyE2ETest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 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.sonar;
+
+import org.glassfish.jersey.internal.sonar.SonarJerseyCommon;
+import org.glassfish.jersey.server.internal.sonar.SonarJerseyServer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class SonarJerseyE2ETest {
+
+    @Test
+    public void testJerseyCommonE2E() {
+        Assert.assertEquals("common e2e", new SonarJerseyCommon().e2e());
+    }
+
+    @Test
+    public void testJerseyCoreE2E() {
+        Assert.assertEquals("server e2e", new SonarJerseyServer().e2e());
+    }
+}
diff --git a/tests/e2e/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable b/tests/e2e/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
new file mode 100644
index 0000000..0d5c7a0
--- /dev/null
+++ b/tests/e2e/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
@@ -0,0 +1,3 @@
+org.glassfish.jersey.tests.e2e.common.AutoDiscoverableTest$CommonAutoDiscoverable
+org.glassfish.jersey.tests.e2e.common.AutoDiscoverableTest$ClientAutoDiscoverable
+org.glassfish.jersey.tests.e2e.common.AutoDiscoverableTest$ServerAutoDiscoverable
\ No newline at end of file
diff --git a/tests/e2e/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable b/tests/e2e/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable
new file mode 100644
index 0000000..c95bd54
--- /dev/null
+++ b/tests/e2e/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable
@@ -0,0 +1,3 @@
+org.glassfish.jersey.tests.e2e.common.ForcedAutoDiscoverableTest$CommonAutoDiscoverable
+org.glassfish.jersey.tests.e2e.common.ForcedAutoDiscoverableTest$ClientAutoDiscoverable
+org.glassfish.jersey.tests.e2e.common.ForcedAutoDiscoverableTest$ServerAutoDiscoverable
diff --git a/tests/e2e/src/test/resources/log4j.properties b/tests/e2e/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7c74efa
--- /dev/null
+++ b/tests/e2e/src/test/resources/log4j.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2017, 2018 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
+#
+
+log4j.rootCategory=ALL, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout.ConversionPattern=[%30.30c{1}] - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
diff --git a/tests/integration/JERSEY-2988/pom.xml b/tests/integration/JERSEY-2988/pom.xml
new file mode 100644
index 0000000..82dc34a
--- /dev/null
+++ b/tests/integration/JERSEY-2988/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.23-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2988</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2988</name>
+
+    <description>
+        Reproducer of JERSEY-2988.
+
+        Jersey does not set locator properly for SingleHk2LocatorManager.
+    </description>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+    </properties>
+
+    <dependencies>
+        <!-- Jersey -->
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Weld -->
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <version>2.0-EDR1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.weld.servlet</groupId>
+            <artifactId>weld-servlet-core</artifactId>
+            <version>3.0.0.Alpha13</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/ContextAwareBean.java b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/ContextAwareBean.java
new file mode 100644
index 0000000..11f7e6a
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/ContextAwareBean.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2988;
+
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class ContextAwareBean {
+
+    @Context
+    private Configuration field;
+
+    private Configuration setter;
+
+    public Response fieldConfig() {
+        return field == null
+                ? Response.serverError().build()
+                : Response.ok().build();
+    }
+
+    public Response setterConfig() {
+        return setter == null
+                ? Response.serverError().build()
+                : Response.ok().build();
+    }
+
+    @Context
+    public void setConfig(Configuration setter) {
+        this.setter = setter;
+    }
+
+}
diff --git a/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/FieldContextExceptionMapper.java b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/FieldContextExceptionMapper.java
new file mode 100644
index 0000000..38ddd30
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/FieldContextExceptionMapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2988;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+public class FieldContextExceptionMapper implements ExceptionMapper<IllegalStateException> {
+
+    @Context
+    private HttpHeaders field;
+
+    @Override
+    public Response toResponse(IllegalStateException ex) {
+        return Response.status(520)
+                .header("x-test-header", field.getHeaderString("x-test-header"))
+                .build();
+    }
+}
diff --git a/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/SetterContextExceptionMapper.java b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/SetterContextExceptionMapper.java
new file mode 100644
index 0000000..c153e9f
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/SetterContextExceptionMapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2988;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+public class SetterContextExceptionMapper implements ExceptionMapper<NullPointerException> {
+
+    private HttpHeaders setter;
+
+    @Context
+    public void setSetter(HttpHeaders setter) {
+        this.setter = setter;
+    }
+
+    @Override
+    public Response toResponse(NullPointerException ex) {
+        return Response.status(520)
+                .header("x-test-header", setter.getHeaderString("x-test-header"))
+                .build();
+    }
+}
diff --git a/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/TestApplication.java b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/TestApplication.java
new file mode 100644
index 0000000..3102bfa
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/TestApplication.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2988;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+@ApplicationPath("/")
+public class TestApplication extends javax.ws.rs.core.Application {
+
+}
diff --git a/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/TestResource.java b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/TestResource.java
new file mode 100644
index 0000000..8c18fe6
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/java/org/glassfish/jersey/tests/integration/jersey2988/TestResource.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2988;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+
+@Path("/test")
+public class TestResource {
+
+    @Inject
+    private ContextAwareBean contextAwareBean;
+
+    @GET
+    @Path("field")
+    public Response field() {
+        return contextAwareBean.fieldConfig();
+    }
+
+    @GET
+    @Path("setter")
+    public Response setter() {
+        return contextAwareBean.setterConfig();
+    }
+
+    @GET
+    @Path("ex/field")
+    public String fieldExceptionMapper() {
+        throw new IllegalStateException("My handled exception.");
+    }
+
+    @GET
+    @Path("ex/setter")
+    public String setterExceptionMapper() {
+        throw new NullPointerException("My handled exception.");
+    }
+
+}
diff --git a/tests/integration/JERSEY-2988/src/main/webapp/WEB-INF/beans.xml b/tests/integration/JERSEY-2988/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..3dcd6e8
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
+       version="1.1" bean-discovery-mode="all">
+
+</beans>
diff --git a/tests/integration/JERSEY-2988/src/main/webapp/WEB-INF/jetty-web.xml b/tests/integration/JERSEY-2988/src/main/webapp/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..eb69254
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/main/webapp/WEB-INF/jetty-web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+    <Set name="serverClasses">
+        <Array type="java.lang.String">
+            <Item>-org.eclipse.jetty.servlet.ServletContextHandler.Decorator</Item>
+        </Array>
+    </Set>
+</Configure>
diff --git a/tests/integration/JERSEY-2988/src/test/java/org/glassfish/jersey/tests/integration/jersey2988/Jersey2988ITCase.java b/tests/integration/JERSEY-2988/src/test/java/org/glassfish/jersey/tests/integration/jersey2988/Jersey2988ITCase.java
new file mode 100644
index 0000000..b5bc49d
--- /dev/null
+++ b/tests/integration/JERSEY-2988/src/test/java/org/glassfish/jersey/tests/integration/jersey2988/Jersey2988ITCase.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2988;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/**
+ * JERSEY-2988 reproducer and JERSEY-2990 (duplicate of the previous one)
+ *
+ * @author Petr Bouda
+ */
+public class Jersey2988ITCase extends JerseyTest {
+
+    private static final String HEADER_NAME = "x-test-header";
+    private static final String HEADER_VALUE = "cool-header";
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Reproducer for JERSEY-2988
+     *
+     * @throws Exception
+     */
+    @Test
+    public void contextFieldInjection() throws Exception {
+        testCdiBeanContextInjection("field");
+    }
+
+    @Test
+    public void contextSetterInjection() throws Exception {
+        testCdiBeanContextInjection("setter");
+    }
+
+    private void testCdiBeanContextInjection(String path) {
+        int status = target("test/" + path).request().get().getStatus();
+        if (status != 200) {
+            fail("@Context field is not properly injected into CDI Bean.");
+        }
+    }
+
+    /**
+     * Reproducer for JERSEY-2990
+     *
+     * @throws Exception
+     */
+    @Test
+    public void contextFieldInjectionExceptionMapper() throws Exception {
+        testExceptionMapperContextInjection("field");
+    }
+
+    @Test
+    public void contextSetterExceptionMapper() throws Exception {
+        testExceptionMapperContextInjection("setter");
+    }
+
+    private void testExceptionMapperContextInjection(String path) {
+        Response response = target("test/ex/" + path).request().header(HEADER_NAME, HEADER_VALUE).get();
+        if (response.getStatus() != 520 || !HEADER_VALUE.equals(response.getHeaderString(HEADER_NAME))) {
+            fail("@Context method was not properly injected into ExceptionMapper.");
+        }
+    }
+}
diff --git a/tests/integration/async-jersey-filter/pom.xml b/tests/integration/async-jersey-filter/pom.xml
new file mode 100644
index 0000000..da8cd9b
--- /dev/null
+++ b/tests/integration/async-jersey-filter/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>async-jersey-filter</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-async-filter</name>
+
+    <description>
+        This web project reproduces tickets:
+        JERSEY-2730
+        JERSEY-2812
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/async/TestApplication.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/async/TestApplication.java
new file mode 100644
index 0000000..f23fa16
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/async/TestApplication.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.async;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.jersey.tests.integration.jersey2730.TestExceptionResource;
+import org.glassfish.jersey.tests.integration.jersey2730.exception.MappedExceptionMapper;
+import org.glassfish.jersey.tests.integration.jersey2812.TestWaitResource;
+
+/**
+ * Jersey application for JERSEY-2730.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestExceptionResource.class);
+        register(MappedExceptionMapper.class);
+        register(TestWaitResource.class);
+    }
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java
new file mode 100644
index 0000000..c531760
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2730;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.servlet.internal.ResponseWriter;
+import org.glassfish.jersey.tests.integration.jersey2730.exception.MappedException;
+import org.glassfish.jersey.tests.integration.jersey2730.exception.UnmappedException;
+import org.glassfish.jersey.tests.integration.jersey2730.exception.UnmappedRuntimeException;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/exception")
+@Singleton
+public class TestExceptionResource {
+
+    /**
+     * An instance of thread that was processing a last request to this resource.
+     */
+    private Thread lastProcessingThread;
+
+    @GET
+    @Path("null")
+    public void get(@Suspended final AsyncResponse asyncResponse) {
+        lastProcessingThread = Thread.currentThread();
+        asyncResponse.resume((Throwable) null);
+    }
+
+    @GET
+    @Path("mapped")
+    public void getMappedException(@Suspended final AsyncResponse asyncResponse) {
+        lastProcessingThread = Thread.currentThread();
+        asyncResponse.resume(new MappedException());
+    }
+
+    @GET
+    @Path("unmapped")
+    public void getUnmappedException(@Suspended final AsyncResponse asyncResponse) {
+        lastProcessingThread = Thread.currentThread();
+        asyncResponse.resume(new UnmappedException());
+    }
+
+    @GET
+    @Path("runtime")
+    public void getUnmappedRuntimeException(@Suspended final AsyncResponse asyncResponse) {
+        lastProcessingThread = Thread.currentThread();
+        asyncResponse.resume(new UnmappedRuntimeException());
+    }
+
+    /**
+     * Returns whether a thread that was processing a last request got stuck in {@link ResponseWriter}.
+     * <p/>
+     * Under normal circumstances, the last processing thread should return back to the servlet container
+     * and its pool.
+     * <p/>
+     * May not work when executed in parallel.
+     *
+     * @return
+     */
+    @GET
+    @Path("rpc/lastthreadstuck")
+    public boolean lastThreadStuckRpc() {
+        if (lastProcessingThread == null || Thread.currentThread() == lastProcessingThread) {
+            return false;
+        }
+
+        switch (lastProcessingThread.getState()) {
+            case BLOCKED:
+            case TIMED_WAITING:
+            case WAITING:
+                for (StackTraceElement stackTraceElement : lastProcessingThread.getStackTrace()) {
+                    if (ResponseWriter.class.getName().equals(stackTraceElement.getClassName())) {
+                        return true;
+                    }
+                }
+        }
+
+        return false;
+    }
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/MappedException.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/MappedException.java
new file mode 100644
index 0000000..cc0ab0f
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/MappedException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2730.exception;
+
+/**
+ * This exception will get mapped to a 432 response with the application exception mapper
+ * implemented by {@link MappedExceptionMapper} class.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class MappedException extends Exception {
+
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/MappedExceptionMapper.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/MappedExceptionMapper.java
new file mode 100644
index 0000000..052dbc3
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/MappedExceptionMapper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2730.exception;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+/**
+ * An exception mapper to return 432 error response when a {@link MappedException} is thrown.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class MappedExceptionMapper implements ExceptionMapper<MappedException> {
+
+    @Override
+    public Response toResponse(MappedException exception) {
+        return Response.status(432).build();
+    }
+
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/UnmappedException.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/UnmappedException.java
new file mode 100644
index 0000000..751872c
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/UnmappedException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2730.exception;
+
+/**
+ * An exception that will be propagated to the servlet container.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class UnmappedException extends Exception {
+
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/UnmappedRuntimeException.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/UnmappedRuntimeException.java
new file mode 100644
index 0000000..d84dc6a
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/exception/UnmappedRuntimeException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2730.exception;
+
+/**
+ * An exception that will be propagated to the servlet container.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class UnmappedRuntimeException extends RuntimeException {
+
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2812/TestFilter.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2812/TestFilter.java
new file mode 100644
index 0000000..9fd3cd9
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2812/TestFilter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2812;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.concurrent.CountDownLatch;
+import java.util.logging.Logger;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * This servlet filter class provides a means to detect whether Jersey (in servlet filter based setup) properly freed the
+ * server-side thread processing the http request to an async RESTful resource where {@link javax.ws.rs.container.AsyncResponse}
+ * wasn't resumed.
+ * <p/>
+ * Reported as JERSEY-2812.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class TestFilter implements Filter {
+
+    private static final Logger LOGGER = Logger.getLogger(TestFilter.class.getName());
+    public static final String CDL_FINISHED = "CDL-finished";
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(final ServletRequest servletRequest,
+                         final ServletResponse servletResponse,
+                         final FilterChain filterChain)
+            throws IOException, ServletException {
+        LOGGER.finest(new Date() + " Thread " + Thread.currentThread().getName() + " is being acquired...");
+
+        final CountDownLatch cdlFinished = new CountDownLatch(1);
+        servletRequest.setAttribute(CDL_FINISHED, cdlFinished);
+
+        filterChain.doFilter(servletRequest, servletResponse);
+
+        // the thread did return from Jersey
+        cdlFinished.countDown();
+
+        LOGGER.finest(new Date() + " Thread " + Thread.currentThread().getName() + " is being released.");
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2812/TestWaitResource.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2812/TestWaitResource.java
new file mode 100644
index 0000000..b457107
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2812/TestWaitResource.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2812;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotAcceptableException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+
+import javax.inject.Singleton;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * This resource provides a way to reproduce JERSEY-2818.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/async")
+@Produces("text/plain")
+@Singleton
+public class TestWaitResource {
+
+    private static final Logger LOGGER = Logger.getLogger(TestWaitResource.class.getName());
+
+    /**
+     * Test context identified by UUID chosen by client.
+     */
+    private final ConcurrentMap<String, TestContext> testContextMap = new ConcurrentHashMap<>();
+
+    private TestContext testContextForUUID(String uuid) {
+        testContextMap.putIfAbsent(uuid, new TestContext());
+        return testContextMap.get(uuid);
+    }
+
+    @GET
+    @Path("wait/{uuid}")
+    public void waitForEvent(@Suspended AsyncResponse asyncResponse,
+                             @Context HttpServletRequest request,
+                             @PathParam("uuid") String uuid) {
+
+        LOGGER.finer("Adding response: " + asyncResponse);
+
+        final TestContext testContext = testContextForUUID(uuid);
+        final CountDownLatch finishedCdl = (CountDownLatch) request.getAttribute(TestFilter.CDL_FINISHED);
+
+        if (finishedCdl == null) {
+            throw new IllegalStateException("The " + TestFilter.class + " was not properly processed before this request!");
+        }
+
+        testContext.asyncResponse = asyncResponse;
+        testContext.finishedCdl = finishedCdl;
+        testContext.startedCdl.countDown();
+
+        LOGGER.finer("Decreasing started cdl: " + testContext.startedCdl);
+    }
+
+    @POST
+    @Path("release/{uuid}")
+    public String releaseLastSuspendedAsyncRequest(@PathParam("uuid") String uuid) {
+
+        LOGGER.finer("Releasing async response");
+
+        if (!testContextMap.containsKey(uuid)) {
+            throw new NotAcceptableException("UUID not found!" + uuid);
+        }
+
+        // clean it up
+        final TestContext releasedTestContext = testContextMap.remove(uuid);
+        releasedTestContext.finishedCdl.countDown();
+        releasedTestContext.startedCdl.countDown();
+        releasedTestContext.asyncResponse.resume("async-OK-" + uuid);
+
+        return "RELEASED";
+    }
+
+    @GET
+    @Path("await/{uuid}/started")
+    public boolean awaitForTheAsyncRequestThreadToStart(@PathParam("uuid") String uuid, @QueryParam("millis") Long millis) {
+        final CountDownLatch startedCdl = testContextForUUID(uuid).startedCdl;
+        try {
+            LOGGER.finer("Checking started cdl: " + startedCdl);
+            return startedCdl.await(millis, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new IllegalStateException("Interrupted while waiting for the thread to finish!", e);
+        }
+    }
+
+    @GET
+    @Path("await/{uuid}/finished")
+    public boolean awaitForTheAsyncRequestThreadToFinish(@PathParam("uuid") String uuid, @QueryParam("millis") Long millis) {
+        if (!testContextMap.containsKey(uuid)) {
+            throw new NotAcceptableException("UUID not found!" + uuid);
+        }
+        try {
+            LOGGER.finer("Decreasing finished cdl: " + testContextMap.get(uuid).finishedCdl);
+            return testContextMap.get(uuid).finishedCdl.await(millis, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new IllegalStateException("Interrupted while waiting for the thread to finish!", e);
+        }
+    }
+
+    /**
+     * Test context holder class.
+     * <p/>
+     * Holds the information for one test identified by UUID chosen by client.
+     *
+     * @see #testContextMap
+     */
+    private static class TestContext {
+
+        /**
+         * This CDL ensures the server-side thread processing the request to /async/wait/{uuid} has started handling the request.
+         */
+        final CountDownLatch startedCdl = new CountDownLatch(1);
+
+        /**
+         * This CDL ensures the server-side thread processing the request to /async/wait/{uuid} was returned to the thread-pool.
+         */
+        volatile CountDownLatch finishedCdl;
+
+        /**
+         * The async response that does get resumed outside of the request to /async/wait/{uuid}. This reproduces the JERSEY-2812
+         * bug.
+         */
+        volatile AsyncResponse asyncResponse;
+    }
+
+}
diff --git a/tests/integration/async-jersey-filter/src/main/webapp/WEB-INF/web.xml b/tests/integration/async-jersey-filter/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d1d166c
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+        metadata-complete="true">
+
+    <filter>
+        <filter-name>jersey2812</filter-name>
+        <filter-class>org.glassfish.jersey.tests.integration.jersey2812.TestFilter</filter-class>
+        <async-supported>true</async-supported>
+    </filter>
+
+    <filter>
+        <filter-name>jersey</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <async-supported>true</async-supported>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.async.TestApplication</param-value>
+        </init-param>
+        <!-- let ServletContainer#doFilter(HttpServletRequest, HttpServletResponse, FilterChain, String, String, String)
+            resolve the response status -->
+        <init-param>
+            <param-name>jersey.config.servlet.filter.forwardOn404</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>jersey2812</filter-name>
+        <url-pattern>/asyncTest/async/wait/*</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>jersey</filter-name>
+        <url-pattern>/exceptionTest/*</url-pattern>
+    </filter-mapping>
+    <filter-mapping>
+        <filter-name>jersey</filter-name>
+        <url-pattern>/asyncTest/*</url-pattern>
+    </filter-mapping>
+
+    <absolute-ordering />
+</web-app>
diff --git a/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/async/AbstractAsyncJerseyTest.java b/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/async/AbstractAsyncJerseyTest.java
new file mode 100644
index 0000000..a2e1927
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/async/AbstractAsyncJerseyTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.async;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class AbstractAsyncJerseyTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestApplication.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+}
diff --git a/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2730/Jersey2730ITCase.java b/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2730/Jersey2730ITCase.java
new file mode 100644
index 0000000..c3ca725
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2730/Jersey2730ITCase.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2730;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.tests.integration.async.AbstractAsyncJerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * JERSEY-2730 reproducer.
+ * <p/>
+ * This test must not run in parallel.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class Jersey2730ITCase extends AbstractAsyncJerseyTest {
+
+    private void assertLastThreadNotStuck() {
+        final boolean lastThreadGotStuck = target("/exceptionTest/exception/rpc/lastthreadstuck").request().get(boolean.class);
+
+        assertFalse("Thread processing last request got stuck while processing the request for "
+                        + TestExceptionResource.class.getCanonicalName(),
+                lastThreadGotStuck);
+    }
+
+    @Test
+    public void asyncResourceNullThrowableReturns500AndDoesNotStuck() throws Exception  {
+        final Response response = target("/exceptionTest/exception/null").request().get();
+
+        assertEquals(500, response.getStatus());
+        assertLastThreadNotStuck();
+    }
+
+    @Test
+    public void asyncResourceUnmappedExceptionReturns500AndDoesNotStuck() throws Exception  {
+        final Response response = target("/exceptionTest/exception/unmapped").request().get();
+
+        assertEquals(500, response.getStatus());
+        assertLastThreadNotStuck();
+    }
+
+    @Test
+    public void asyncResourceUnmappedRuntimeExceptionReturns500AndDoesNotStuck() throws Exception  {
+        final Response response = target("/exceptionTest/exception/runtime").request().get();
+
+        assertEquals(500, response.getStatus());
+        assertLastThreadNotStuck();
+    }
+
+    @Test
+    public void asyncResourceMappedExceptionReturns432() throws Exception  {
+        final Response response = target("/exceptionTest/exception/mapped").request().get();
+
+        assertEquals(432, response.getStatus());
+        assertLastThreadNotStuck();
+    }
+
+    @Test
+    public void asyncResourceNonExistentReturns404() throws Exception {
+        final Response response = target("/exceptionTest/exception/notfound").request().get();
+
+        assertEquals(404, response.getStatus());
+    }
+
+}
diff --git a/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2812/Jersey2812ITCase.java b/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2812/Jersey2812ITCase.java
new file mode 100644
index 0000000..d86a8f4
--- /dev/null
+++ b/tests/integration/async-jersey-filter/src/test/java/org/glassfish/jersey/tests/integration/jersey2812/Jersey2812ITCase.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2812;
+
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.core.Response;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.tests.integration.async.AbstractAsyncJerseyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * JERSEY-2812 reproducer.
+ * <p/>
+ * This test must not run in parallel.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class Jersey2812ITCase extends AbstractAsyncJerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(Jersey2812ITCase.class.getName());
+    private static long WAIT_TIMEOUT = 5000;
+
+    private AtomicReference<String> asyncResult = new AtomicReference<>();
+    private String uuid = UUID.randomUUID().toString();
+    private ExecutorService executorService = Executors
+            .newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).build());
+
+    @Before
+    public void triggerTheWaitRequestInSeparateThread() throws Exception {
+        executorService.execute(new Runnable() {
+            @Override
+            public void run() {
+                LOGGER.finer("Running a request to /async/wait in a separate thread.");
+                asyncResult.set(target("/asyncTest/async/wait").path(uuid).request().get(String.class));
+            }
+        });
+    }
+
+    /**
+     * Tests whether the server-side thread that is processing a http request to the servlet-filter-based Jersey setup ends up
+     * stuck or returned back to the pool of available threads.
+     * <p/>
+     * This test prevents a regression reported in JERSEY-2812.
+     * <p/>
+     * When the {@link javax.ws.rs.container.AsyncResponse} was left intact in the RESTful resource (as implemented in {@link
+     * TestWaitResource#waitForEvent(AsyncResponse, HttpServletRequest, String)}), the server-side Jersey thread ended up in
+     * {@link org.glassfish.jersey.servlet.internal.ResponseWriter#getResponseContext()} blocked because of the resolution of http
+     * response status from {@link org.glassfish.jersey.servlet.ServletContainer#doFilter(HttpServletRequest, HttpServletResponse,
+     * FilterChain, String, String, String)}
+     * <p/>
+     * This test uses a separate thread to call {@code /async/wait/{uuid}} resource which blocks until the {@code
+     * /async/release/{uuid}} is called. In the meantime the JUnit thread calls {@code /async/await/{uuid}} to discover whether
+     * the server-side thread processing the request to {@code /async/await/{uuid}/started} did start processing of the request.
+     * Consecutively, the JUnit thread calls {@code /async/await/{uuid}/finished} with a timeout {@code #WAIT_TIMEOUT} to discover
+     * whether the server-side thread got stuck (which is what JERSEY-2812 reported) or not.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void asyncSuspendedResourceDoesNotGetStuck() throws Exception {
+        // [1] wait for the /async/wait request to be processed
+        final Response startResponse = target("/asyncTest/async/await").path(uuid).path("started")
+                .queryParam("millis", WAIT_TIMEOUT).request().get();
+        assertTrue("The server-side thread handling the request to /async/wait didn't start in timely fashion. "
+                        + "This error indicates this test is not executed / designed properly rather than a regression in "
+                        + "JERSEY-2812 fix.",
+                startResponse.readEntity(Boolean.class));
+
+        // [2] wait for the /async/wait request to finish
+        final Response finishResponse = target("/asyncTest/async/await").path(uuid).path("finished")
+                .queryParam("millis", WAIT_TIMEOUT).request().get();
+        assertTrue("The thread processing the /async/wait request did not respond in timely fashion. "
+                        + "Memory leak / thread got stuck detected!",
+                finishResponse.readEntity(Boolean.class));
+
+        // [3] release the blocked http call to /async/wait
+        final String releaseResponse = target("/asyncTest/async/release").path(uuid).request().post(null, String.class);
+        assertEquals("RELEASED", releaseResponse);
+
+        // [4] test whether everything ended as expected
+        executorService.shutdown();
+        assertTrue("The test thread did not finish in timely fashion!",
+                executorService.awaitTermination(WAIT_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertEquals("async-OK-" + uuid, asyncResult.get());
+    }
+
+    @After
+    public void releaseResources() {
+        // release the server-side thread regardless of whether left un-attended
+        target("/asyncTest/async/release").path(uuid).request().post(null);
+    }
+
+    @After
+    public void terminateThread() {
+        // forcibly terminate the test client thread
+        executorService.shutdownNow();
+    }
+
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/pom.xml b/tests/integration/cdi-beanvalidation-webapp/pom.xml
new file mode 100644
index 0000000..df201d3
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/pom.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-beanvalidation-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-beanvalidation-webapp</name>
+
+    <description>Jersey CDI/Bean Validation test web application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator-cdi</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-weld2-se</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x-validation</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+    </profiles>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiApplication.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiApplication.java
new file mode 100644
index 0000000..6adcc12
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiApplication.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources.
+ * This one is configured as a CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/cdi")
+@ApplicationScoped
+public class CdiApplication extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(CdiFieldInjectedResource.class);
+            add(CdiParamInjectedResource.class);
+            add(CdiPropertyInjectedResource.class);
+            add(CdiOldFashionedResource.class);
+
+            add(CdiValidationInterceptor.class);
+        }};
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiFieldInjectedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiFieldInjectedResource.java
new file mode 100644
index 0000000..31d05cf
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiFieldInjectedResource.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.context.RequestScoped;
+
+import javax.inject.Inject;
+import javax.validation.ConstraintViolationException;
+import javax.validation.constraints.NotNull;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * This CDI backed resource should get validated and validation result field injected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("validated/field")
+@RequestScoped
+public class CdiFieldInjectedResource {
+
+    @Inject
+    ValidationResult validationResult;
+
+    @Inject
+    NonJaxRsValidatedBean cdiBean;
+
+    @QueryParam("q")
+    @NotNull
+    String q;
+
+    /**
+     * Return number of validation issues.
+     *
+     * @return value from field injected validation bean.
+     */
+    @Path("validate")
+    @GET
+    public int getValidate() {
+
+        return validationResult.getViolationCount();
+    }
+
+    /**
+     * Return number of validation issues for non JAX-RS bean.
+     * This is to make sure the implicit Hibernate validator
+     * is functioning for raw CDI beans, where Jersey
+     * is not involved in method invocation.
+     *
+     * @return number of validation issues revealed when invoking injected CDI bean.
+     */
+    @Path("validate/non-jaxrs")
+    @GET
+    public int getValidateNonJaxRs(@QueryParam("h") String h) {
+
+        try {
+            cdiBean.echo(h);
+            return 0;
+        } catch (ConstraintViolationException ex) {
+            return ex.getConstraintViolations().size();
+        }
+    }
+
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiOldFashionedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiOldFashionedResource.java
new file mode 100644
index 0000000..b7a5803
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiOldFashionedResource.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.validation.constraints.NotNull;
+
+/**
+ * This CDI backed resource should get validated.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("old/fashioned")
+@RequestScoped
+public class CdiOldFashionedResource {
+
+    /**
+     * Query param getter.
+     *
+     * @return query param value.
+     */
+    @Path("validate")
+    @GET
+    public String getQ(@QueryParam("q") @NotNull String q) {
+
+        return q;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiParamInjectedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiParamInjectedResource.java
new file mode 100644
index 0000000..18f74c7
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiParamInjectedResource.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.context.RequestScoped;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+
+/**
+ * This CDI backed resource should get validated.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("validated/param")
+@RequestScoped
+public class CdiParamInjectedResource {
+
+    /**
+     * Return number of validation issues.
+     *
+     * @return value from param injected validation bean.
+     */
+    @Path("validate")
+    @GET
+    public int getValidate(@QueryParam("q") @NotNull String q, @Context ValidationResult validationResult) {
+
+        return validationResult.getViolationCount();
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiPropertyInjectedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiPropertyInjectedResource.java
new file mode 100644
index 0000000..2af8a87
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiPropertyInjectedResource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.Interceptors;
+import javax.interceptor.InvocationContext;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * This CDI backed resource should get validated and validation result property injected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("validated/property")
+@RequestScoped
+public class CdiPropertyInjectedResource {
+
+    private ValidationResult validationResult;
+
+    @Inject
+    public void setValidationResult(ValidationResult validationResult) {
+        this.validationResult = validationResult;
+    }
+
+    public ValidationResult getValidationResult() {
+        return validationResult;
+    }
+
+    /**
+     * Return number of validation issues.
+     *
+     * @return value from field injected validation bean.
+     */
+    @Path("validate")
+    @GET
+    public int getValidate(@QueryParam("q") @NotNull String q) {
+
+        return getValidationResult().getViolationCount();
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationInterceptor.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationInterceptor.java
new file mode 100644
index 0000000..d3f9d9d
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationInterceptor.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.validation.ConstraintViolationException;
+import javax.validation.ValidationException;
+
+import org.glassfish.jersey.server.spi.ValidationInterceptor;
+import org.glassfish.jersey.server.spi.ValidationInterceptorContext;
+
+import org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy;
+
+/**
+ * CDI backed interceptor to handle validation issues.
+ *
+ * @author Jaku Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+public class CdiValidationInterceptor implements ValidationInterceptor {
+
+    private final CdiValidationResult validationResult;
+    private final CdiPropertyInjectedResource pir;
+
+    /**
+     * Empty constructor to make CDI happy.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public CdiValidationInterceptor() {
+        this.validationResult = null;
+        this.pir = null;
+    }
+
+    /**
+     * Injection constructor.
+     *
+     * @param validationResult  CDI implementation of validation result.
+     * @param resource CDI property-injected JAX-RS resource.
+     */
+    @Inject
+    public CdiValidationInterceptor(CdiValidationResult validationResult, CdiPropertyInjectedResource resource) {
+        this.validationResult = validationResult;
+        this.pir = resource;
+    }
+
+    @Override
+    public void onValidate(ValidationInterceptorContext ctx) throws ValidationException {
+
+        final Object resource = ctx.getResource();
+        if (resource instanceof TargetInstanceProxy) {
+            ctx.setResource(((TargetInstanceProxy) resource).getTargetInstance());
+        }
+
+        try {
+            ctx.proceed();
+        } catch (ConstraintViolationException constraintViolationException) {
+
+            // First check for a property
+            if (ValidationResultUtil.hasValidationResultProperty(resource)) {
+                final Method validationResultGetter = ValidationResultUtil.getValidationResultGetter(resource);
+                ValidationResultUtil.updateValidationResultProperty(resource, validationResultGetter,
+                        constraintViolationException.getConstraintViolations());
+                pir.setValidationResult(validationResult);
+            } else {
+                // Then check for a field
+                final Field vr = ValidationResultUtil.getValidationResultField(resource);
+                if (vr != null) {
+                    // we have the right guy, no need to use reflection:
+                    validationResult.setViolations(constraintViolationException.getConstraintViolations());
+                } else {
+                    if (isValidationResultInArgs(ctx.getArgs())) {
+                        this.validationResult.setViolations(constraintViolationException.getConstraintViolations());
+                    } else {
+                        throw constraintViolationException;
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean isValidationResultInArgs(Object[] args) {
+        for (Object a : args) {
+            if (a != null) {
+                Class<?> argClass = a.getClass();
+                do {
+                    if (ValidationResult.class.isAssignableFrom(argClass)) {
+                        return true;
+                    }
+                    argClass = argClass.getSuperclass();
+                } while (argClass != Object.class);
+            }
+        }
+        return false;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationResult.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationResult.java
new file mode 100644
index 0000000..0bed3ff
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationResult.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.context.RequestScoped;
+import javax.validation.ConstraintViolation;
+
+/**
+ * CDI implementation of validation result.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+public class CdiValidationResult implements ValidationResult {
+
+    private Set<ConstraintViolation<?>> constraintViolationSet;
+
+    public void setViolations(Set<ConstraintViolation<?>> violations) {
+        this.constraintViolationSet = new HashSet<>(violations);
+    }
+
+    @Override
+    public Set<ConstraintViolation<?>> getAllViolations() {
+        return constraintViolationSet;
+    }
+
+    @Override
+    public boolean isFailed() {
+        return (constraintViolationSet != null) ? !constraintViolationSet.isEmpty() : false;
+    }
+
+    @Override
+    public int getViolationCount() {
+        return (constraintViolationSet != null) ? constraintViolationSet.size() : 0;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationResultBinder.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationResultBinder.java
new file mode 100644
index 0000000..52bdb79
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/CdiValidationResultBinder.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.util.Set;
+
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.Extension;
+import javax.inject.Inject;
+
+import org.glassfish.jersey.ext.cdi1x.internal.CdiUtil;
+import org.glassfish.jersey.ext.cdi1x.internal.GenericCdiBeanSupplier;
+import org.glassfish.jersey.internal.inject.Binding;
+import org.glassfish.jersey.internal.inject.Bindings;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.server.spi.ComponentProvider;
+
+/**
+ * Utility that binds HK2 factory to provide CDI managed validation result bean.
+ * This is to make sure validation result could be injected as a resource method parameter
+ * even when running in CDI environment.
+ */
+public class CdiValidationResultBinder implements Extension, ComponentProvider {
+
+    @Inject
+    BeanManager beanManager;
+
+    InjectionManager injectionManager;
+
+    @Override
+    public void initialize(InjectionManager injectionManager) {
+        this.injectionManager = injectionManager;
+        this.beanManager = CdiUtil.getBeanManager();
+    }
+
+    @Override
+    public boolean bind(Class<?> component, Set<Class<?>> providerContracts) {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void done() {
+        if (beanManager != null) { // in CDI environment
+            Binding binding = Bindings
+                    .supplier(new GenericCdiBeanSupplier(
+                            CdiValidationResult.class, injectionManager, beanManager, true))
+                    .to(CdiValidationResult.class)
+                    .to(ValidationResult.class);
+
+            injectionManager.register(binding);
+        }
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2Application.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2Application.java
new file mode 100644
index 0000000..e6efb11
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2Application.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.ws.rs.ApplicationPath;
+
+import javax.enterprise.inject.Vetoed;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application to configure resources.
+ * This one will get fully managed by HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/hk2")
+@Vetoed
+public class Hk2Application extends ResourceConfig {
+
+    public Hk2Application() {
+        super(Hk2ParamInjectedResource.class,
+                Hk2FieldInjectedResource.class,
+                Hk2PropertyInjectedResource.class,
+                Hk2OldFashionedResource.class);
+
+        register(new Hk2ValidationInterceptor.Binder());
+        register(new AbstractBinder(){
+
+            @Override
+            protected void configure() {
+                bindAsContract(Hk2ValidationResult.class).to(ValidationResult.class).in(RequestScoped.class);
+            }
+        });
+    }
+
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2FieldInjectedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2FieldInjectedResource.java
new file mode 100644
index 0000000..3d3b850
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2FieldInjectedResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.inject.Vetoed;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+
+/**
+ * This one should get validated.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("validated/field")
+@Vetoed
+public class Hk2FieldInjectedResource {
+
+    @QueryParam("q")
+    @NotNull
+    String q;
+
+    @Context
+    ValidationResult validationResult;
+
+    /**
+     * Return number of validation issues.
+     *
+     * @return value from field produced bean.
+     */
+    @Path("validate")
+    @GET
+    public int getValidate() {
+        return validationResult.getViolationCount();
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2OldFashionedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2OldFashionedResource.java
new file mode 100644
index 0000000..a1c8596
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2OldFashionedResource.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.inject.Vetoed;
+import javax.validation.constraints.NotNull;
+
+/**
+ * This HK2 managed resource should get validated.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("old/fashioned")
+@Vetoed
+public class Hk2OldFashionedResource {
+
+    /**
+     * Query param getter.
+     *
+     * @return query parameter value.
+     */
+    @Path("validate")
+    @GET
+    public String getQ(@QueryParam("q") @NotNull String q) {
+
+        return q;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ParamInjectedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ParamInjectedResource.java
new file mode 100644
index 0000000..ee9a6fb
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ParamInjectedResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.inject.Vetoed;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+
+/**
+ * This HK2 managed resource should get validated and validation
+ * result injected via resource method parameter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("validated/param")
+@Vetoed
+public class Hk2ParamInjectedResource {
+
+    /**
+     * Return number of validation issues.
+     *
+     * @return value from field produced bean.
+     */
+    @Path("validate")
+    @GET
+    public int getValidate(@QueryParam("q") @NotNull String q, @Context Hk2ValidationResult validationResult) {
+
+        return validationResult.getViolationCount();
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2PropertyInjectedResource.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2PropertyInjectedResource.java
new file mode 100644
index 0000000..ba02b09
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2PropertyInjectedResource.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.inject.Vetoed;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+
+/**
+ * This one should get validated.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("validated/property")
+@Vetoed
+public class Hk2PropertyInjectedResource {
+
+    @QueryParam("q")
+    @NotNull
+    String q;
+
+    ValidationResult validationResult;
+
+    public ValidationResult getValidationResult() {
+        return validationResult;
+    }
+
+    @Context
+    public void setValidationResult(ValidationResult validationResult) {
+        this.validationResult = validationResult;
+    }
+
+    /**
+     * Return number of validation issues.
+     *
+     * @return value from field produced bean.
+     */
+    @Path("validate")
+    @GET
+    public int getValidate() {
+        return validationResult.getViolationCount();
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ValidationInterceptor.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ValidationInterceptor.java
new file mode 100644
index 0000000..985453d
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ValidationInterceptor.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.function.Supplier;
+
+import javax.ws.rs.core.Context;
+
+import javax.enterprise.inject.Vetoed;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.validation.ConstraintViolationException;
+import javax.validation.ValidationException;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.server.spi.ValidationInterceptor;
+import org.glassfish.jersey.server.spi.ValidationInterceptorContext;
+
+/**
+ * HK2 managed validation interceptor.
+ */
+@Vetoed
+public class Hk2ValidationInterceptor implements ValidationInterceptor {
+
+
+    private final Provider<Hk2ValidationResult> validationResult;
+
+    public Hk2ValidationInterceptor(Provider<Hk2ValidationResult> validationResult) {
+        this.validationResult = validationResult;
+    }
+
+    public static class Binder extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+            bindFactory(ValidationInterceptorFactory.class, Singleton.class)
+                    .to(ValidationInterceptor.class);
+        }
+
+    }
+
+    private static class ValidationInterceptorFactory implements Supplier<ValidationInterceptor> {
+
+        @Inject
+        Provider<Hk2ValidationResult> validationResultProvider;
+
+        @Override
+        public ValidationInterceptor get() {
+            return new Hk2ValidationInterceptor(validationResultProvider);
+        }
+    }
+
+    @Override
+    public void onValidate(
+            ValidationInterceptorContext ctx) throws ValidationException {
+        try {
+            ctx.proceed();
+        } catch (ConstraintViolationException ex) {
+            ensureValidationResultInjected(ctx, ex);
+            validationResult.get().setViolations(ex.getConstraintViolations());
+        }
+    }
+
+    private void ensureValidationResultInjected(
+            final ValidationInterceptorContext ctx, final ConstraintViolationException ex) {
+
+        if (!isValidationResultInArgs(ctx.getArgs())
+                && !isValidationResultInResource(ctx)
+                && !hasValidationResultProperty(ctx.getResource())) {
+
+            throw ex;
+        }
+    }
+
+    private boolean isValidationResultInResource(ValidationInterceptorContext ctx) {
+        Class<?> clazz = ctx.getResource().getClass();
+        do {
+            for (Field f : clazz.getDeclaredFields()) {
+                // Of ValidationResult and JAX-RS injectable
+                if (ValidationResult.class.isAssignableFrom(f.getType())
+                        && f.getAnnotation(Context.class) != null) {
+                    return true;
+                }
+            }
+            clazz = clazz.getSuperclass();
+        } while (clazz != Object.class);
+        return false;
+    }
+
+    private boolean isValidationResultInArgs(Object[] args) {
+        for (Object a : args) {
+            if (a != null && ValidationResult.class.isAssignableFrom(a.getClass())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines if a resource has a property of type {@code javax.mvc.validation.ValidationResult}.
+     *
+     * @param resource resource instance.
+     * @return outcome of test.
+     */
+    public static boolean hasValidationResultProperty(final Object resource) {
+        return getValidationResultGetter(resource) != null && getValidationResultSetter(resource) != null;
+    }
+
+    /**
+     * Returns a getter for {@code javax.mvc.validation.ValidationResult} or {@code null}
+     * if one cannot be found.
+     *
+     * @param resource resource instance.
+     * @return getter or {@code null} if not available.
+     */
+    public static Method getValidationResultGetter(final Object resource) {
+        Class<?> clazz = resource.getClass();
+        do {
+            for (Method m : clazz.getDeclaredMethods()) {
+                if (isValidationResultGetter(m)) {
+                    return m;
+                }
+            }
+            clazz = clazz.getSuperclass();
+        } while (clazz != Object.class);
+        return null;
+    }
+
+    /**
+     * Determines if a method is a getter for {@code javax.mvc.validation.ValidationResult}.
+     *
+     * @param m method to test.
+     * @return outcome of test.
+     */
+    private static boolean isValidationResultGetter(Method m) {
+        return m.getName().startsWith("get")
+                && ValidationResult.class.isAssignableFrom(m.getReturnType())
+                && Modifier.isPublic(m.getModifiers()) && m.getParameterTypes().length == 0;
+    }
+
+    /**
+     * Returns a setter for {@code javax.mvc.validation.ValidationResult} or {@code null}
+     * if one cannot be found.
+     *
+     * @param resource resource instance.
+     * @return setter or {@code null} if not available.
+     */
+    public static Method getValidationResultSetter(final Object resource) {
+        Class<?> clazz = resource.getClass();
+        do {
+            for (Method m : clazz.getDeclaredMethods()) {
+                if (isValidationResultSetter(m)) {
+                    return m;
+                }
+            }
+            clazz = clazz.getSuperclass();
+        } while (clazz != Object.class);
+        return null;
+    }
+
+    /**
+     * Determines if a method is a setter for {@code javax.mvc.validation.ValidationResult}.
+     * As a CDI initializer method, it must be annotated with {@link javax.inject.Inject}.
+     *
+     * @param m method to test.
+     * @return outcome of test.
+     */
+    private static boolean isValidationResultSetter(Method m) {
+        return m.getName().startsWith("set") && m.getParameterTypes().length == 1
+                && ValidationResult.class.isAssignableFrom(m.getParameterTypes()[0])
+                && m.getReturnType() == Void.TYPE && Modifier.isPublic(m.getModifiers())
+                && m.getAnnotation(Context.class) != null;
+    }
+
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ValidationResult.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ValidationResult.java
new file mode 100644
index 0000000..f286105
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/Hk2ValidationResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.inject.Vetoed;
+import javax.validation.ConstraintViolation;
+
+/**
+ * HK2 managed validation result bean.
+ */
+@Vetoed
+public class Hk2ValidationResult implements ValidationResult {
+
+    Set<ConstraintViolation<?>> constraintViolationSet;
+
+    public void setViolations(Set<ConstraintViolation<?>> violations) {
+        this.constraintViolationSet = new HashSet<>(violations);
+    }
+
+    @Override
+    public Set<ConstraintViolation<?>> getAllViolations() {
+        return constraintViolationSet;
+    }
+
+    @Override
+    public boolean isFailed() {
+        return (constraintViolationSet != null) ? !constraintViolationSet.isEmpty() : false;
+    }
+
+    @Override
+    public int getViolationCount() {
+        return (constraintViolationSet != null) ? constraintViolationSet.size() : 0;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/NonJaxRsValidatedBean.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/NonJaxRsValidatedBean.java
new file mode 100644
index 0000000..110aca3
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/NonJaxRsValidatedBean.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.enterprise.context.RequestScoped;
+import javax.validation.constraints.NotNull;
+
+/**
+ * CDI bean that gets validated by Hibernate validator directly.
+ * This is to ensure Jersey interceptor wrapper
+ * does not block the original validator from functioning
+ * on raw CDI beans.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+public class NonJaxRsValidatedBean {
+
+    public String echo(@NotNull String value) {
+        return value;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/ValidationResult.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/ValidationResult.java
new file mode 100644
index 0000000..b723793
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/ValidationResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+
+/**
+ * An interface to be utilized when resource method validation issues
+ * are to be handled within actual resource method.
+ */
+public interface ValidationResult {
+
+    /**
+     * Returns an immutable set of all constraint violations detected.
+     *
+     * @return All constraint violations detected
+     */
+    public Set<ConstraintViolation<?>> getAllViolations();
+
+    /**
+     * Returns <code>true</code> if there is at least one constraint violation.
+     * Same as checking whether {@link #getViolationCount()} is greater than zero.
+     *
+     * @return <code>true</code> if there is at least one constraint violation.
+     */
+    public boolean isFailed();
+
+    /**
+     * Returns the total number of constraint violations detected. Same as calling
+     * <code>getAllViolations().size()</code>.
+     *
+     * @return The number of constraint violations
+     */
+    int getViolationCount();
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/ValidationResultUtil.java b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/ValidationResultUtil.java
new file mode 100644
index 0000000..906da13
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/java/org/glassfish/jersey/tests/cdi/bv/ValidationResultUtil.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.bv;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+
+import javax.enterprise.inject.Vetoed;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.validation.ConstraintViolation;
+
+/**
+ * Helper class to implement support for {@code javax.mvc.validation.ValidationResult}.
+ *
+ * @author Santiago Pericas-Geertsen
+ */
+@Vetoed
+public final class ValidationResultUtil {
+
+    private static final String VALIDATION_RESULT = ValidationResult.class.getName();
+
+    private ValidationResultUtil() {
+        throw new AssertionError("Instantiation not allowed.");
+    }
+
+    /**
+     * Search for a {@code javax.mvc.validation.ValidationResult} field in the resource's
+     * class hierarchy. Field must be annotated with {@link javax.inject.Inject}.
+     *
+     * @param resource resource instance.
+     * @return field or {@code null} if none is found.
+     */
+    public static Field getValidationResultField(final Object resource) {
+        Class<?> clazz = resource.getClass();
+        do {
+            for (Field f : clazz.getDeclaredFields()) {
+                // Of ValidationResult and CDI injectable
+                if (f.getType().getName().equals(VALIDATION_RESULT)
+                        && f.getAnnotation(Inject.class) != null) {
+                    return f;
+                }
+            }
+            clazz = clazz.getSuperclass();
+        } while (clazz != Object.class);
+        return null;
+    }
+
+    /**
+     * Updates a {@code javax.mvc.validation.ValidationResult} field. In pseudo-code:
+     * <p/>
+     * resource.field.setViolations(constraints)
+     *
+     * @param resource    resource instance.
+     * @param field       field to be updated.
+     * @param constraints new set of constraints.
+     */
+    public static void updateValidationResultField(Object resource, Field field,
+                                                   Set<ConstraintViolation<?>> constraints) {
+        try {
+            field.setAccessible(true);
+            final Object obj = field.get(resource);
+            Method setter;
+            try {
+                setter = obj.getClass().getMethod("setViolations", Set.class);
+            } catch (NoSuchMethodException e) {
+                setter = obj.getClass().getSuperclass().getMethod("setViolations", Set.class);
+            }
+            setter.invoke(obj, constraints);
+        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+            // ignore for now
+            System.out.println("Damn it...");
+        } catch (Throwable t) {
+            System.out.println("What the heck...");
+        }
+    }
+
+    /**
+     * Updates a {@code javax.mvc.validation.ValidationResult} property. In pseudo-code:
+     * <p/>
+     * obj = getter.invoke(resource);
+     * obj.setViolations(constraints);
+     * setter.invoke(resource, obj);
+     *
+     * @param resource    resource instance.
+     * @param getter      getter to be used.
+     * @param constraints new set of constraints.
+     */
+    public static void updateValidationResultProperty(Object resource, Method getter,
+                                                      Set<ConstraintViolation<?>> constraints) {
+        try {
+            final Object obj = getter.invoke(resource);
+            Method setViolations;
+            try {
+                setViolations = obj.getClass().getMethod("setViolations", Set.class);
+            } catch (NoSuchMethodException e) {
+                setViolations = obj.getClass().getSuperclass().getMethod("setViolations", Set.class);
+            }
+            setViolations.invoke(obj, constraints);
+
+            final Method setter = getValidationResultSetter(resource);
+
+            if (setter != null) {
+                setter.invoke(resource, obj);
+            }
+        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+            // ignore for now
+        }
+    }
+
+    /**
+     * Determines if a resource has a property of type {@code javax.mvc.validation.ValidationResult}.
+     *
+     * @param resource resource instance.
+     * @return outcome of test.
+     */
+    public static boolean hasValidationResultProperty(final Object resource) {
+        return getValidationResultGetter(resource) != null && getValidationResultSetter(resource) != null;
+    }
+
+    /**
+     * Returns a getter for {@code javax.mvc.validation.ValidationResult} or {@code null}
+     * if one cannot be found.
+     *
+     * @param resource resource instance.
+     * @return getter or {@code null} if not available.
+     */
+    public static Method getValidationResultGetter(final Object resource) {
+        Class<?> clazz = resource.getClass();
+        do {
+            for (Method m : clazz.getDeclaredMethods()) {
+                if (isValidationResultGetter(m)) {
+                    return m;
+                }
+            }
+            clazz = clazz.getSuperclass();
+        } while (clazz != Object.class);
+        return null;
+    }
+
+    /**
+     * Determines if a method is a getter for {@code javax.mvc.validation.ValidationResult}.
+     *
+     * @param m method to test.
+     * @return outcome of test.
+     */
+    private static boolean isValidationResultGetter(Method m) {
+        return m.getName().startsWith("get")
+                && m.getReturnType().getName().equals(VALIDATION_RESULT)
+                && Modifier.isPublic(m.getModifiers()) && m.getParameterTypes().length == 0;
+    }
+
+    /**
+     * Returns a setter for {@code javax.mvc.validation.ValidationResult} or {@code null}
+     * if one cannot be found.
+     *
+     * @param resource resource instance.
+     * @return setter or {@code null} if not available.
+     */
+    public static Method getValidationResultSetter(final Object resource) {
+        return getValidationResultSetter(resource.getClass());
+    }
+
+    private static Method getValidationResultSetter(final Class<?> resourceClass) {
+        Class<?> clazz = resourceClass;
+        do {
+            for (Method m : clazz.getDeclaredMethods()) {
+                if (isValidationResultSetter(m)) {
+                    return m;
+                }
+            }
+            clazz = clazz.getSuperclass();
+        } while (clazz != Object.class);
+        return null;
+    }
+
+    /**
+     * Determines if a method is a setter for {@code javax.mvc.validation.ValidationResult}.
+     * As a CDI initializer method, it must be annotated with {@link javax.inject.Inject}.
+     *
+     * @param m method to test.
+     * @return outcome of test.
+     */
+    private static boolean isValidationResultSetter(Method m) {
+        return m.getName().startsWith("set") && m.getParameterTypes().length == 1
+                && m.getParameterTypes()[0].getName().equals(VALIDATION_RESULT)
+                && m.getReturnType() == Void.TYPE && Modifier.isPublic(m.getModifiers())
+                && m.getAnnotation(Inject.class) != null;
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/beans.xml b/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..d036d62
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.cdi.bv.CdiValidationResultBinder
\ No newline at end of file
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider b/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
new file mode 100644
index 0000000..d036d62
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.cdi.bv.CdiValidationResultBinder
\ No newline at end of file
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-beanvalidation-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..1d9cc6c
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans/>
+
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/BaseValidationTest.java b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/BaseValidationTest.java
new file mode 100644
index 0000000..f2eb3fb
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/BaseValidationTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Common test for resource validation. The same set of tests is used
+ * for the following scenarios: Grizzly based combined deployment with CDI enabled,
+ * WAR based combined deployment with CDI enabled, Grizzly based deployment without CDI enabled.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public abstract class BaseValidationTest extends JerseyTest {
+
+    public abstract String getAppPath();
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-beanvalidation-webapp").path(getAppPath()).build();
+    }
+
+    @Test
+    public void testParamValidatedResourceNoParam() throws Exception {
+        _testParamValidatedResourceNoParam(target());
+    }
+
+    public static void _testParamValidatedResourceNoParam(final WebTarget target) throws Exception {
+
+        Integer errors = target.register(LoggingFeature.class)
+                .path("validated").path("param").path("validate")
+                .request().get(Integer.class);
+
+        assertThat(errors, is(1));
+    }
+
+    @Test
+    public void testParamValidatedResourceParamProvided() throws Exception {
+        _testParamValidatedResourceParamProvided(target());
+    }
+
+    public static void _testParamValidatedResourceParamProvided(WebTarget target) throws Exception {
+        Integer errors = target.register(LoggingFeature.class).path("validated").path("field").path("validate")
+                .queryParam("q", "one").request().get(Integer.class);
+        assertThat(errors, is(0));
+    }
+
+    @Test
+    public void testFieldValidatedResourceNoParam() throws Exception {
+        _testFieldValidatedResourceNoParam(target());
+    }
+
+    public static void _testFieldValidatedResourceNoParam(final WebTarget target) throws Exception {
+
+        Integer errors = target.register(LoggingFeature.class)
+                .path("validated").path("field").path("validate")
+                .request().get(Integer.class);
+
+        assertThat(errors, is(1));
+    }
+
+    @Test
+    public void testFieldValidatedResourceParamProvided() throws Exception {
+        _testFieldValidatedResourceParamProvided(target());
+    }
+
+    public static void _testFieldValidatedResourceParamProvided(final WebTarget target) throws Exception {
+        Integer errors = target.register(LoggingFeature.class).path("validated").path("field").path("validate")
+                .queryParam("q", "one").request().get(Integer.class);
+        assertThat(errors, is(0));
+    }
+
+    @Test
+    public void testPropertyValidatedResourceNoParam() throws Exception {
+        _testPropertyValidatedResourceNoParam(target());
+    }
+
+    public static void _testPropertyValidatedResourceNoParam(final WebTarget target) throws Exception {
+
+        Integer errors = target.register(LoggingFeature.class)
+                .path("validated").path("property").path("validate")
+                .request().get(Integer.class);
+
+        assertThat(errors, is(1));
+    }
+
+    @Test
+    public void testPropertyValidatedResourceParamProvided() throws Exception {
+        _testPropertyValidatedResourceParamProvided(target());
+    }
+
+    public static void _testPropertyValidatedResourceParamProvided(final WebTarget target) throws Exception {
+        Integer errors = target.register(LoggingFeature.class).path("validated").path("property").path("validate")
+                .queryParam("q", "one").request().get(Integer.class);
+        assertThat(errors, is(0));
+    }
+
+    @Test
+    public void testOldFashionedResourceNoParam() {
+        _testOldFashionedResourceNoParam(target());
+    }
+
+    public static void _testOldFashionedResourceNoParam(final WebTarget target) {
+
+        Response response = target.register(LoggingFeature.class)
+                .path("old").path("fashioned").path("validate")
+                .request().get();
+
+        assertThat(response.getStatus(), is(400));
+    }
+
+    @Test
+    public void testOldFashionedResourceParamProvided() throws Exception {
+        _testOldFashionedResourceParamProvided(target());
+    }
+
+    public static void _testOldFashionedResourceParamProvided(final WebTarget target) throws Exception {
+        String response = target.register(LoggingFeature.class).path("old").path("fashioned").path("validate")
+                .queryParam("q", "one").request().get(String.class);
+        assertThat(response, is("one"));
+    }
+
+    public static void _testNonJaxRsValidationFieldValidatedResourceNoParam(final WebTarget target) {
+        Integer errors = target.register(LoggingFeature.class)
+                .path("validated").path("field").path("validate").path("non-jaxrs")
+                .queryParam("q", "not-important-just-to-get-this-through-jax-rs").request().get(Integer.class);
+
+        assertThat(errors, is(1));
+    }
+
+    public static void _testNonJaxRsValidationFieldValidatedResourceParamProvided(final WebTarget target) {
+        Integer errors = target.register(LoggingFeature.class)
+                .path("validated").path("field").path("validate").path("non-jaxrs")
+                .queryParam("q", "not-important-just-to-get-this-through-jax-rs")
+                .queryParam("h", "bummer")
+                .request().get(Integer.class);
+
+        assertThat(errors, is(0));
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/CombinedTest.java b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/CombinedTest.java
new file mode 100644
index 0000000..847b0ef
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/CombinedTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Properties;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainerProvider;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.hamcrest.CoreMatchers;
+import org.jboss.weld.environment.se.Weld;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test both Jersey apps running simultaneously within a single Grizzly HTTP server
+ * to make sure injection managers do not interfere. The test is not executed
+ * if other than the default (Grizzly) test container has been set.
+ * For Servlet based container testing, the other two tests, {@link RawCdiTest} and {@link RawHk2Test},
+ * do the same job, because the WAR application contains both Jersey apps already.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CombinedTest {
+
+    public static final String CDI_URI = "/cdi";
+    public static final String HK2_URI = "/hk2";
+
+    public static final String PORT_NUMBER = getSystemProperty(TestProperties.CONTAINER_PORT,
+                                                    Integer.toString(TestProperties.DEFAULT_CONTAINER_PORT));
+
+    private static final URI BASE_HK2_URI = URI.create("http://localhost:" + PORT_NUMBER + HK2_URI);
+    private static final URI BASE_CDI_URI = URI.create("http://localhost:" + PORT_NUMBER + CDI_URI);
+
+    private static final boolean isDefaultTestContainerFactorySet = isDefaultTestContainerFactorySet();
+
+    Weld weld;
+    HttpServer cdiServer;
+
+    Client client;
+    WebTarget cdiTarget, hk2Target;
+
+    @Before
+    public void before() throws IOException {
+        if (isDefaultTestContainerFactorySet && Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            initializeWeld();
+            startGrizzlyContainer();
+            initializeClient();
+        }
+    }
+
+    @Before
+    public void beforeIsImmediate() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @After
+    public void after() {
+        if (isDefaultTestContainerFactorySet && Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            cdiServer.shutdownNow();
+            weld.shutdown();
+            client.close();
+        }
+    }
+
+    @Test
+    public void testParamValidatedResourceNoParam() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testParamValidatedResourceNoParam(cdiTarget);
+        BaseValidationTest._testParamValidatedResourceNoParam(hk2Target);
+    }
+
+    @Test
+    public void testParamValidatedResourceParamProvided() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testParamValidatedResourceParamProvided(cdiTarget);
+        BaseValidationTest._testParamValidatedResourceParamProvided(hk2Target);
+    }
+
+    @Test
+    public void testFieldValidatedResourceNoParam() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testFieldValidatedResourceNoParam(cdiTarget);
+        BaseValidationTest._testFieldValidatedResourceNoParam(hk2Target);
+    }
+
+    @Test
+    public void testFieldValidatedResourceParamProvided() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testFieldValidatedResourceParamProvided(cdiTarget);
+        BaseValidationTest._testFieldValidatedResourceParamProvided(hk2Target);
+    }
+
+    @Test
+    public void testPropertyValidatedResourceNoParam() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testPropertyValidatedResourceNoParam(cdiTarget);
+        BaseValidationTest._testPropertyValidatedResourceNoParam(hk2Target);
+    }
+
+    @Test
+    public void testPropertyValidatedResourceParamProvided() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testPropertyValidatedResourceParamProvided(cdiTarget);
+        BaseValidationTest._testPropertyValidatedResourceParamProvided(hk2Target);
+    }
+
+    @Test
+    public void testOldFashionedResourceNoParam() {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testOldFashionedResourceNoParam(cdiTarget);
+        BaseValidationTest._testOldFashionedResourceNoParam(hk2Target);
+    }
+
+    @Test
+    public void testOldFashionedResourceParamProvided() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testOldFashionedResourceParamProvided(cdiTarget);
+        BaseValidationTest._testOldFashionedResourceParamProvided(hk2Target);
+    }
+
+    @Test
+    public void testNonJaxRsValidationFieldValidatedResourceNoParam() {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testNonJaxRsValidationFieldValidatedResourceNoParam(cdiTarget);
+    }
+
+    @Test
+    public void testNonJaxRsValidationFieldValidatedResourceParamProvided() {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        BaseValidationTest._testNonJaxRsValidationFieldValidatedResourceParamProvided(cdiTarget);
+    }
+
+    private void initializeWeld() {
+        weld = new Weld();
+        weld.initialize();
+    }
+
+    private void startGrizzlyContainer() throws IOException {
+        final ResourceConfig cdiConfig = ResourceConfig.forApplicationClass(CdiApplication.class);
+        final ResourceConfig hk2Config = ResourceConfig.forApplicationClass(Hk2Application.class);
+
+        cdiServer = GrizzlyHttpServerFactory.createHttpServer(BASE_CDI_URI, cdiConfig, false);
+        final HttpHandler hk2Handler = createGrizzlyContainer(hk2Config);
+        cdiServer.getServerConfiguration().addHttpHandler(hk2Handler, HK2_URI);
+        cdiServer.start();
+    }
+
+    private void initializeClient() {
+        client = ClientBuilder.newClient();
+        cdiTarget = client.target(BASE_CDI_URI);
+        hk2Target = client.target(BASE_HK2_URI);
+    }
+
+    private GrizzlyHttpContainer createGrizzlyContainer(ResourceConfig resourceConfig) {
+        return (new GrizzlyHttpContainerProvider()).createContainer(GrizzlyHttpContainer.class, resourceConfig);
+    }
+
+    private static boolean isDefaultTestContainerFactorySet() {
+        final String testContainerFactory = getSystemProperty(TestProperties.CONTAINER_FACTORY, null);
+        return testContainerFactory == null || TestProperties.DEFAULT_CONTAINER_FACTORY.equals(testContainerFactory);
+    }
+
+    private static String getSystemProperty(final String propertyName, final String defaultValue) {
+        final Properties systemProperties = System.getProperties();
+        return systemProperties.getProperty(propertyName, defaultValue);
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/RawCdiTest.java b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/RawCdiTest.java
new file mode 100644
index 0000000..d0b996c
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/RawCdiTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+
+import org.jboss.weld.environment.se.Weld;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * Validation result test for CDI environment.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class RawCdiTest extends BaseValidationTest {
+
+    Weld weld;
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        if (Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            if (!ExternalTestContainerFactory.class.isAssignableFrom(getTestContainerFactory().getClass())) {
+                weld = new Weld();
+                weld.initialize();
+            }
+            super.setUp();
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            if (!ExternalTestContainerFactory.class.isAssignableFrom(getTestContainerFactory().getClass())) {
+                weld.shutdown();
+            }
+            super.tearDown();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return ResourceConfig.forApplicationClass(CdiApplication.class);
+    }
+
+    @Override
+    public String getAppPath() {
+        return "cdi";
+    }
+
+    @Test
+    public void testNonJaxRsValidationFieldValidatedResourceNoParam() {
+        BaseValidationTest._testNonJaxRsValidationFieldValidatedResourceNoParam(target());
+    }
+
+    @Test
+    public void testNonJaxRsValidationFieldValidatedResourceParamProvided() {
+        BaseValidationTest._testNonJaxRsValidationFieldValidatedResourceParamProvided(target());
+    }
+}
diff --git a/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/RawHk2Test.java b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/RawHk2Test.java
new file mode 100644
index 0000000..f83ea61
--- /dev/null
+++ b/tests/integration/cdi-beanvalidation-webapp/src/test/java/org/glassfish/jersey/tests/cdi/bv/RawHk2Test.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.bv;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Validation result test for raw HK2 environment.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class RawHk2Test extends BaseValidationTest {
+
+    @Override
+    protected Application configure() {
+        return ResourceConfig.forApplicationClass(Hk2Application.class);
+    }
+
+    @Override
+    public String getAppPath() {
+        return "hk2";
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/pom.xml b/tests/integration/cdi-ejb-test-webapp/pom.xml
new file mode 100644
index 0000000..39b0521
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-ejb-test-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-ejb-webapp</name>
+
+    <description>Jersey CDI/EJB test web application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/BasicTimer.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/BasicTimer.java
new file mode 100644
index 0000000..775fae0
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/BasicTimer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * Basic timer implementation to be reused for various types of beans.
+ *
+ * @author Jakub Podlesak
+ */
+public abstract class BasicTimer {
+
+    long ms;
+
+   /**
+    * Provide information on internal timer millisecond value.
+    *
+    * @return milliseconds when the current bean has been post-constructed.
+    */
+   public long getMiliseconds() {
+       return ms;
+   }
+
+   /**
+    * Initialize this timer with the current time.
+    */
+   @PostConstruct
+   public void init() {
+       this.ms = System.currentTimeMillis();
+   }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiAppScopedTimer.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiAppScopedTimer.java
new file mode 100644
index 0000000..259e669
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiAppScopedTimer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Application scoped CDI bean to be injected into EJB resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+public class CdiAppScopedTimer extends BasicTimer {
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiRequestScopedResource.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiRequestScopedResource.java
new file mode 100644
index 0000000..a2e6204
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiRequestScopedResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ejb.EJB;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Request scoped CDI bean injected with EJB timers.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-scoped")
+public class CdiRequestScopedResource {
+
+    @EJB EjbSingletonTimer ejbInjectedTimer;
+    @Inject EjbSingletonTimer jsr330InjectedTimer;
+    @Inject CdiAppScopedTimer cdiTimer;
+
+    @GET
+    @Path("ejb-injected-timer")
+    public String getEjbTime() {
+        return Long.toString(ejbInjectedTimer.getMiliseconds());
+    }
+
+    @GET
+    @Path("jsr330-injected-timer")
+    public String getJsr330Time() {
+        return Long.toString(jsr330InjectedTimer.getMiliseconds());
+    }
+
+    @GET
+    public String getMyself() {
+        return this.toString();
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiRequestScopedTimer.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiRequestScopedTimer.java
new file mode 100644
index 0000000..f48e1f4
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiRequestScopedTimer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Request scoped CDI timer.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+public class CdiRequestScopedTimer extends BasicTimer {
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbSingletonResource.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbSingletonResource.java
new file mode 100644
index 0000000..b3472d9
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbSingletonResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ejb.Singleton;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * EJB singleton session bean injected with CDI timers.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+@Path("singleton")
+public class EjbSingletonResource {
+
+    @Inject CdiRequestScopedTimer requestScopedTimer;
+    @Inject CdiAppScopedTimer appScopedTimer;
+
+    @GET
+    @Path("request-scoped-timer")
+    public String getReqTime() {
+        return Long.toString(requestScopedTimer.getMiliseconds());
+    }
+
+    @GET
+    @Path("app-scoped-timer")
+    public String getAppTime() {
+        return Long.toString(appScopedTimer.getMiliseconds());
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbSingletonTimer.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbSingletonTimer.java
new file mode 100644
index 0000000..3bec04d
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbSingletonTimer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ejb.Singleton;
+
+/**
+ * EJB singleton timer.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+public class EjbSingletonTimer extends BasicTimer {
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatefulResource.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatefulResource.java
new file mode 100644
index 0000000..25fbc9f
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatefulResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ejb.Stateful;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * EJB backed JAX-RS resource injected with CDI service providers.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateful
+@Path("stateful")
+public class EjbStatefulResource {
+
+    @Inject CdiRequestScopedTimer requestScopedTimer;
+    @Inject CdiAppScopedTimer appScopedTimer;
+
+    @GET
+    @Path("request-scoped-timer")
+    public String getReqTime() {
+        return Long.toString(requestScopedTimer.getMiliseconds());
+    }
+
+    @GET
+    @Path("app-scoped-timer")
+    public String getAppTime() {
+        return Long.toString(appScopedTimer.getMiliseconds());
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatelessResource.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatelessResource.java
new file mode 100644
index 0000000..71da1ce
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatelessResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * EJB session bean injected with CDI service providers.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@Path("stateless")
+public class EjbStatelessResource {
+
+    @Inject CdiRequestScopedTimer requestScopedTimer;
+    @Inject CdiAppScopedTimer appScopedTimer;
+
+    @GET
+    @Path("request-scoped-timer")
+    public String getReqTime() {
+        return Long.toString(requestScopedTimer.getMiliseconds());
+    }
+
+    @GET
+    @Path("app-scoped-timer")
+    public String getAppTime() {
+        return Long.toString(appScopedTimer.getMiliseconds());
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatelessTimer.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatelessTimer.java
new file mode 100644
index 0000000..721aa18
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EjbStatelessTimer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ejb.Stateless;
+
+/**
+ * EJB session timer bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+public class EjbStatelessTimer extends BasicTimer {
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..69e395d
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(CdiRequestScopedResource.class);
+        classes.add(CdiRequestScopedTimer.class);
+        classes.add(CdiAppScopedTimer.class);
+        classes.add(EjbStatelessResource.class);
+        classes.add(EjbStatefulResource.class);
+        classes.add(EjbSingletonResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-ejb-test-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-ejb-test-webapp/src/main/webapp/WEB-INF/web.xml b/tests/integration/cdi-ejb-test-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..71e6b6e
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+</web-app>
diff --git a/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiIntoEjbTest.java b/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiIntoEjbTest.java
new file mode 100644
index 0000000..d0e8226
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiIntoEjbTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test CDI timers injected into EJB beans.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiIntoEjbTest extends TestBase {
+
+    @Test
+    public void testStateless() {
+        _testRequestScoped("stateless");
+        _testAppScoped("stateless");
+    }
+
+    @Test
+    public void testStateful() {
+        _testRequestScoped("stateful");
+        _testAppScoped("stateful");
+    }
+
+    @Test
+    public void testSingleton() {
+        _testRequestScoped("singleton");
+        _testAppScoped("singleton");
+    }
+
+    private void _testRequestScoped(final String ejbType) {
+
+        final WebTarget target = target().path(ejbType).path("request-scoped-timer");
+        long firstMillis = _getMillis(target);
+        sleep(2);
+        long secondMillis = _getMillis(target);
+
+        assertTrue("Second request should have greater millis!", secondMillis > firstMillis);
+    }
+
+    private void _testAppScoped(final String ejbType) {
+
+        final WebTarget target = target().path(ejbType).path("app-scoped-timer");
+        long firstMillis = _getMillis(target);
+        sleep(2);
+        long secondMillis = _getMillis(target);
+
+        assertTrue("Second request should have the same millis!", secondMillis == firstMillis);
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/EjbIntoCdiTest.java b/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/EjbIntoCdiTest.java
new file mode 100644
index 0000000..231836c
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/EjbIntoCdiTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+/**
+ * Test EJB timers injected into CDI beans.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class EjbIntoCdiTest extends TestBase {
+
+    @Test
+    public void testInjection() {
+
+        final WebTarget cdiResource = target().path("request-scoped");
+        final WebTarget ejbInjectedTimer = cdiResource.path("ejb-injected-timer");
+        final WebTarget jsr330InjectedTimer = cdiResource.path("jsr330-injected-timer");
+
+        String firstResource = cdiResource.request().get(String.class);
+        long firstMillis = _getMillis(ejbInjectedTimer);
+        sleep(2);
+        String secondResource = cdiResource.request().get(String.class);
+        long secondMillis = _getMillis(jsr330InjectedTimer);
+
+        assertThat(firstMillis, equalTo(secondMillis));
+        assertThat(firstResource, not(equalTo(secondResource)));
+    }
+}
diff --git a/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/TestBase.java b/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/TestBase.java
new file mode 100644
index 0000000..d11189d
--- /dev/null
+++ b/tests/integration/cdi-ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/TestBase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-test-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class TestBase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-ejb-test-webapp").build();
+    }
+
+    protected long _getMillis(final WebTarget target) throws NumberFormatException {
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), is(200));
+        return Long.decode(response.readEntity(String.class));
+    }
+
+    protected void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ex) {
+            Logger.getLogger(CdiIntoEjbTest.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
+
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml
new file mode 100644
index 0000000..d0cf29f
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-iface-with-non-jaxrs-impl-test-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-iface-with-non-jaxrs-impl-test-webapp</name>
+
+    <description>Jersey CDI using non JAX-RS implementation for CDI injection</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+         <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiEcho.java b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiEcho.java
new file mode 100644
index 0000000..217a999
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiEcho.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * Raw CDI implementation of {@link Echo} service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiEcho implements Echo {
+
+    @Override
+    public String echo(String s) {
+        return String.format("CDI ECHOED %s", s);
+    }
+}
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Echo.java b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Echo.java
new file mode 100644
index 0000000..0173d6c
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Echo.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * Echo service interface.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface Echo {
+
+    /**
+     * Echo service method;
+     *
+     * @param s input
+     * @return echoed input
+     */
+    public String echo(String s);
+}
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoResource.java b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoResource.java
new file mode 100644
index 0000000..5ca0c7c
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoResource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * JAX-RS resource class backed by CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("echo")
+public class EchoResource {
+
+    // CDI injection, HK2 should back off
+    // even if EchoImpl is not directly used here
+    // and could appear as HK2 custom bound type
+    // to Jersey CDI extension
+    @Inject Echo echoService;
+
+    @GET
+    public String cdiEcho(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+}
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..a5506a8
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<>();
+        classes.add(EchoResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/webapp/WEB-INF/web.xml b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..71e6b6e
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+</web-app>
diff --git a/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/EchoResourceTest.java b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/EchoResourceTest.java
new file mode 100644
index 0000000..dfe58b0
--- /dev/null
+++ b/tests/integration/cdi-iface-with-non-jaxrs-impl-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/EchoResourceTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.startsWith;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-test-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class EchoResourceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-iface-with-non-jaxrs-impl-test-webapp").build();
+    }
+
+    @Test
+    public void testCdiInjection() throws Exception {
+        final Response response = target().path("echo").queryParam("s", "I").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), startsWith("CDI ECHOED"));
+    }
+
+    protected void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ex) {
+            Logger.getLogger(EchoResourceTest.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+}
diff --git a/tests/integration/cdi-multimodule/ear/pom.xml b/tests/integration/cdi-multimodule/ear/pom.xml
new file mode 100644
index 0000000..5b01d1e
--- /dev/null
+++ b/tests/integration/cdi-multimodule/ear/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>cdi-multimodule-ear</artifactId>
+    <packaging>ear</packaging>
+    <name>jersey-tests-integration-cdi-multimodule-ear</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-ear-plugin</artifactId>
+                <configuration>
+                    <version>6</version>
+                    <defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
+                    <modules>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>cdi-multimodule-war1</artifactId>
+                        </webModule>
+                         <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>cdi-multimodule-war2</artifactId>
+                        </webModule>
+                        <jarModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>cdi-multimodule-lib</artifactId>
+                        </jarModule>
+                    </modules>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>cdi-multimodule-war1</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+         <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>cdi-multimodule-war2</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>cdi-multimodule-lib</artifactId>
+            <type>jar</type>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/integration/cdi-multimodule/lib/pom.xml b/tests/integration/cdi-multimodule/lib/pom.xml
new file mode 100644
index 0000000..4e38c98
--- /dev/null
+++ b/tests/integration/cdi-multimodule/lib/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>cdi-multimodule-lib</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-integration-cdi-multimodule-lib</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/cdi-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/lib/JaxRsInjectedDependentBean.java b/tests/integration/cdi-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/lib/JaxRsInjectedDependentBean.java
new file mode 100644
index 0000000..44eca4a
--- /dev/null
+++ b/tests/integration/cdi-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/lib/JaxRsInjectedDependentBean.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.cdi.lib;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * CDI managed bean, that gets JAX-RS injected. This bean is being consumed
+ * by all web apps within an EAR packaged enterprise application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsInjectedDependentBean {
+
+    @Context
+    UriInfo uriInfo;
+
+    @HeaderParam("x-test")
+    String testHeader;
+
+    /**
+     * Get me URI info.
+     *
+     * @return URI info from the JAX-RS layer.
+     */
+    public UriInfo getUriInfo() {
+        return uriInfo;
+    }
+
+    /**
+     * Get me the actual request test header.
+     *
+     * @return actual request URI info.
+     */
+    public String getTestHeader() {
+        return testHeader;
+    }
+
+}
diff --git a/tests/integration/cdi-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/lib/JaxRsInjectedRequestScopedBean.java b/tests/integration/cdi-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/lib/JaxRsInjectedRequestScopedBean.java
new file mode 100644
index 0000000..5664ed8
--- /dev/null
+++ b/tests/integration/cdi-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/lib/JaxRsInjectedRequestScopedBean.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.cdi.lib;
+
+import javax.enterprise.context.RequestScoped;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * CDI managed bean, that gets JAX-RS injected. This bean is being consumed
+ * by all web apps within an EAR packaged enterprise application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+public class JaxRsInjectedRequestScopedBean {
+
+    @Context
+    UriInfo uriInfo;
+
+    @HeaderParam("x-test")
+    String testHeader;
+
+    /**
+     * Get me the actual JAX-RS request URI info.
+     *
+     * @return actual request URI info.
+     */
+    public UriInfo getUriInfo() {
+        return uriInfo;
+    }
+
+    /**
+     * Get me the actual request test header.
+     *
+     * @return actual request URI info.
+     */
+    public String getTestHeader() {
+        return testHeader;
+    }
+}
diff --git a/tests/integration/cdi-multimodule/lib/src/main/resources/META-INF/beans.xml b/tests/integration/cdi-multimodule/lib/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..3b46d69
--- /dev/null
+++ b/tests/integration/cdi-multimodule/lib/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-multimodule/pom.xml b/tests/integration/cdi-multimodule/pom.xml
new file mode 100644
index 0000000..803f972
--- /dev/null
+++ b/tests/integration/cdi-multimodule/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-multimodule</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-integration-cdi-multimodule</name>
+
+    <description>
+        CDI Multi-Module
+    </description>
+
+    <modules>
+        <module>ear</module>
+        <module>lib</module>
+        <module>war1</module>
+        <module>war2</module>
+    </modules>
+</project>
diff --git a/tests/integration/cdi-multimodule/war1/pom.xml b/tests/integration/cdi-multimodule/war1/pom.xml
new file mode 100644
index 0000000..6c9cea8
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war1/pom.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>cdi-multimodule-war1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-cdi-multimodule-war</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>3.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>cdi-multimodule-lib</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/AppScopedJaxRsResource.java b/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/AppScopedJaxRsResource.java
new file mode 100644
index 0000000..3a1a69c
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/AppScopedJaxRsResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.cdi.web1;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedDependentBean;
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedRequestScopedBean;
+
+/**
+ * JAX-RS resource backed by an application scoped CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+  */
+@Path("app-scoped")
+@ApplicationScoped
+public class AppScopedJaxRsResource {
+
+    @Inject
+    JaxRsInjectedRequestScopedBean reqScopedBean;
+
+    @Path("req/header")
+    @GET
+    public String getReqHeader() {
+        return reqScopedBean.getTestHeader();
+    }
+
+    @Path("req/uri/{p}")
+    @GET
+    public String getReqUri() {
+        return reqScopedBean.getUriInfo().getRequestUri().toString();
+    }
+
+}
diff --git a/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/JaxRsApp.java b/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/JaxRsApp.java
new file mode 100644
index 0000000..f8b64da
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/JaxRsApp.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.cdi.web1;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application resource configuration.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/")
+public class JaxRsApp extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(RequestScopedJaxRsResource.class);
+            add(AppScopedJaxRsResource.class);
+        }};
+    }
+}
diff --git a/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/RequestScopedJaxRsResource.java b/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/RequestScopedJaxRsResource.java
new file mode 100644
index 0000000..5f0ff3e
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/RequestScopedJaxRsResource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.cdi.web1;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedDependentBean;
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedRequestScopedBean;
+
+/**
+ * JAX-RS resource backed by a request scoped CDI bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+  */
+@Path("request-scoped")
+@RequestScoped
+public class RequestScopedJaxRsResource {
+
+    @Inject
+    JaxRsInjectedDependentBean dependentBean;
+
+    @Inject
+    JaxRsInjectedRequestScopedBean reqScopedBean;
+
+    @Path("req/header")
+    @GET
+    public String getReqHeader() {
+        return reqScopedBean.getTestHeader();
+    }
+
+    @Path("dependent/header")
+    @GET
+    public String getDependentHeader() {
+        return dependentBean.getTestHeader();
+    }
+
+    @Path("req/uri/{p}")
+    @GET
+    public String getReqUri() {
+        return reqScopedBean.getUriInfo().getRequestUri().toString();
+    }
+
+    @Path("dependent/uri/{p}")
+    @GET
+    public String getAppUri() {
+        return dependentBean.getUriInfo().getRequestUri().toString();
+    }
+}
diff --git a/tests/integration/cdi-multimodule/war1/src/main/webapp/index.jsp b/tests/integration/cdi-multimodule/war1/src/main/webapp/index.jsp
new file mode 100644
index 0000000..d9c02e5
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war1/src/main/webapp/index.jsp
@@ -0,0 +1,31 @@
+<%--
+
+    Copyright (c) 2015, 2018 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
+
+--%>
+
+<%@page contentType="text/html" pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>JSP Page</title>
+    </head>
+    <body>
+        <h1>Hello World!</h1>
+    </body>
+</html>
diff --git a/tests/integration/cdi-multimodule/war1/src/test/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/JaxRsCdiIntegrationTest.java b/tests/integration/cdi-multimodule/war1/src/test/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/JaxRsCdiIntegrationTest.java
new file mode 100644
index 0000000..62cf293
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war1/src/test/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web1/JaxRsCdiIntegrationTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.cdi.web1;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test for CDI web application resources. The JAX-RS resources use CDI components from a library jar.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsCdiIntegrationTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApp();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-multimodule-war1").build();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testUriInfoInjectionReqScopedResourceDependentBean() {
+        _testResource("request-scoped/dependent");
+    }
+
+    @Test
+    public void testUriInfoInjectionReqScopedResourceRequestScopedBean() {
+        _testResource("request-scoped/req");
+    }
+
+    @Test
+    public void testUriInfoInjectionAppScopedResourceRequestScopedBean() {
+        _testResource("app-scoped/req");
+    }
+
+    private void _testResource(String resourcePath) {
+        _testUriInfo(resourcePath);
+        _testHeader(resourcePath);
+    }
+
+    private void _testUriInfo(String resourcePath) {
+
+        _testSinglePathUriUnfo(resourcePath, "one");
+        _testSinglePathUriUnfo(resourcePath, "two");
+        _testSinglePathUriUnfo(resourcePath, "three");
+    }
+
+    private void _testSinglePathUriUnfo(final String resourcePath, final String pathParam) {
+
+        final URI baseUri = getBaseUri();
+        final String expectedResult = baseUri.resolve(resourcePath + "/uri/" + pathParam).toString();
+
+        final Response response = target().path(resourcePath).path("uri").path(pathParam).request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), equalTo(expectedResult));
+    }
+
+    private void _testHeader(final String resourcePath) {
+
+        _testSingleHeader(resourcePath, "one");
+        _testSingleHeader(resourcePath, "two");
+        _testSingleHeader(resourcePath, "three");
+    }
+
+    private void _testSingleHeader(final String resourcePath, final String headerValue) {
+
+        final String expectedResult = headerValue;
+
+        final Response response = target().path(resourcePath).path("header").request().header("x-test", headerValue).get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), equalTo(expectedResult));
+    }
+}
diff --git a/tests/integration/cdi-multimodule/war2/pom.xml b/tests/integration/cdi-multimodule/war2/pom.xml
new file mode 100644
index 0000000..0c4352b
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>cdi-multimodule-war2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-cdi-multimodule-war2</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>cdi-multimodule-lib</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsAppOne.java b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsAppOne.java
new file mode 100644
index 0000000..11d5cb3
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsAppOne.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.cdi.web2;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * First JAX-RS application resource configuration.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("one")
+public class JaxRsAppOne extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(SharedRequestScopedJaxRsResource.class);
+            add(SharedAppScopedJaxRsResource.class);
+        }};
+    }
+}
+
diff --git a/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsAppTwo.java b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsAppTwo.java
new file mode 100644
index 0000000..0dbe770
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsAppTwo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.cdi.web2;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Second JAX-RS application resource configuration.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("two")
+public class JaxRsAppTwo extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(SharedRequestScopedJaxRsResource.class);
+            add(SharedAppScopedJaxRsResource.class);
+        }};
+    }
+}
+
diff --git a/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/SharedAppScopedJaxRsResource.java b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/SharedAppScopedJaxRsResource.java
new file mode 100644
index 0000000..ba722b7
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/SharedAppScopedJaxRsResource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.cdi.web2;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedDependentBean;
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedRequestScopedBean;
+
+/**
+ * JAX-RS resource backed by an application scoped CDI bean.
+ * This one is being shared between the two JAX-RS apps
+ * {@link JaxRsAppOne} and {@link JaxRsAppTwo}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("app-scoped")
+@ApplicationScoped
+public class SharedAppScopedJaxRsResource {
+
+    @Inject
+    JaxRsInjectedDependentBean dependentBean;
+
+    @Inject
+    JaxRsInjectedRequestScopedBean reqScopedBean;
+
+    @Path("req/header")
+    @GET
+    public String getReqHeader() {
+        return reqScopedBean.getTestHeader();
+    }
+
+    @Path("dependent/header")
+    @GET
+    public String getDependentHeader() {
+        return dependentBean.getTestHeader();
+    }
+
+    @Path("req/uri/{p}")
+    @GET
+    public String getReqUri() {
+        return reqScopedBean.getUriInfo().getRequestUri().toString();
+    }
+
+    @Path("dependent/uri/{p}")
+    @GET
+    public String getAppUri() {
+        return dependentBean.getUriInfo().getRequestUri().toString();
+    }
+}
diff --git a/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/SharedRequestScopedJaxRsResource.java b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/SharedRequestScopedJaxRsResource.java
new file mode 100644
index 0000000..876ee7f
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/SharedRequestScopedJaxRsResource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.cdi.web2;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedDependentBean;
+import org.glassfish.jersey.tests.integration.multimodule.cdi.lib.JaxRsInjectedRequestScopedBean;
+
+/**
+ * JAX-RS resource backed by a request scoped CDI bean.
+ * This one is being shared between the two JAX-RS apps
+ * {@link JaxRsAppOne} and {@link JaxRsAppTwo}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+  */
+@Path("request-scoped")
+@RequestScoped
+public class SharedRequestScopedJaxRsResource {
+
+    @Inject
+    JaxRsInjectedDependentBean dependentBean;
+
+    @Inject
+    JaxRsInjectedRequestScopedBean reqScopedBean;
+
+    @Path("req/header")
+    @GET
+    public String getReqHeader() {
+        return reqScopedBean.getTestHeader();
+    }
+
+    @Path("dependent/header")
+    @GET
+    public String getDependentHeader() {
+        return dependentBean.getTestHeader();
+    }
+
+    @Path("req/uri/{p}")
+    @GET
+    public String getReqUri() {
+        return reqScopedBean.getUriInfo().getRequestUri().toString();
+    }
+
+    @Path("dependent/uri/{p}")
+    @GET
+    public String getAppUri() {
+        return dependentBean.getUriInfo().getRequestUri().toString();
+    }
+}
diff --git a/tests/integration/cdi-multimodule/war2/src/main/webapp/index.jsp b/tests/integration/cdi-multimodule/war2/src/main/webapp/index.jsp
new file mode 100644
index 0000000..d9c02e5
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/src/main/webapp/index.jsp
@@ -0,0 +1,31 @@
+<%--
+
+    Copyright (c) 2015, 2018 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
+
+--%>
+
+<%@page contentType="text/html" pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>JSP Page</title>
+    </head>
+    <body>
+        <h1>Hello World!</h1>
+    </body>
+</html>
diff --git a/tests/integration/cdi-multimodule/war2/src/test/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsCdiIntegrationTest.java b/tests/integration/cdi-multimodule/war2/src/test/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsCdiIntegrationTest.java
new file mode 100644
index 0000000..033f953
--- /dev/null
+++ b/tests/integration/cdi-multimodule/war2/src/test/java/org/glassfish/jersey/tests/integration/multimodule/cdi/web2/JaxRsCdiIntegrationTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.cdi.web2;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test for CDI web application resources. The JAX-RS resources use CDI components from a library jar.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsCdiIntegrationTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsAppOne();
+    }
+
+//    @Override
+//    protected URI getBaseUri() {
+//        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-multimodule-war1").build();
+//    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testUriInfoInjectionReqScopedResourceDependentBean() {
+
+        _testResource("cdi-multimodule-war2/one/request-scoped/dependent");
+        _testResource("cdi-multimodule-war2/two/request-scoped/dependent");
+    }
+
+    @Test
+    public void testUriInfoInjectionReqScopedResourceRequestScopedBean() {
+
+        _testResource("cdi-multimodule-war2/one/request-scoped/req");
+        _testResource("cdi-multimodule-war2/two/request-scoped/req");
+    }
+
+    @Test
+    public void testUriInfoInjectionAppScopedResourceRequestScopedBean() {
+
+        _testResource("cdi-multimodule-war2/one/app-scoped/req");
+        _testResource("cdi-multimodule-war2/two/app-scoped/req");
+    }
+
+    @Ignore("until JERSEY-2914 gets resolved")
+    @Test
+    public void testUriInfoInjectionAppScopedResourceDependentBean() {
+
+        _testResource("cdi-multimodule-war2/one/app-scoped/dependent");
+        _testResource("cdi-multimodule-war2/two/app-scoped/dependent");
+    }
+
+    private void _testResource(String resourcePath) {
+        _testUriInfo(resourcePath);
+        _testHeader(resourcePath);
+    }
+
+    private void _testUriInfo(String resourcePath) {
+
+        _testSinglePathUriUnfo(resourcePath, "one");
+        _testSinglePathUriUnfo(resourcePath, "two");
+        _testSinglePathUriUnfo(resourcePath, "three");
+    }
+
+    private void _testSinglePathUriUnfo(final String resourcePath, final String pathParam) {
+
+        final URI baseUri = getBaseUri();
+        final String expectedResult = baseUri.resolve(resourcePath + "/uri/" + pathParam).toString();
+
+        final Response response = target().path(resourcePath).path("uri").path(pathParam).request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), equalTo(expectedResult));
+    }
+
+    private void _testHeader(final String resourcePath) {
+
+        _testSingleHeader(resourcePath, "one");
+        _testSingleHeader(resourcePath, "two");
+        _testSingleHeader(resourcePath, "three");
+    }
+
+    private void _testSingleHeader(final String resourcePath, final String headerValue) {
+
+        final String expectedResult = headerValue;
+
+        final Response response = target().path(resourcePath).path("header").request().header("x-test", headerValue).get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), equalTo(expectedResult));
+    }
+}
diff --git a/tests/integration/cdi-multipart-webapp/pom.xml b/tests/integration/cdi-multipart-webapp/pom.xml
new file mode 100644
index 0000000..79f0bf4
--- /dev/null
+++ b/tests/integration/cdi-multipart-webapp/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-multipart-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-multipart-webapp</name>
+
+    <description>Jersey CDI test web application that uses multipart feature</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-multipart-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoResource.java b/tests/integration/cdi-multipart-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoResource.java
new file mode 100644
index 0000000..d64a263
--- /dev/null
+++ b/tests/integration/cdi-multipart-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+
+
+/**
+ * GF-21033 reproducer. Just a resource using multi-part support.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/echo")
+@RequestScoped
+public class EchoResource {
+
+    /**
+     * We want to consume form data using multi-part provider.
+     *
+     * @param input form data
+     * @return input data
+     */
+    @POST
+    public Response echoMultipart(@FormDataParam ("input") String input) {
+        return Response.ok(input).build();
+    }
+}
diff --git a/tests/integration/cdi-multipart-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-multipart-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..e730754
--- /dev/null
+++ b/tests/integration/cdi-multipart-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+
+/**
+ * GF-21033 reproducer. This is to make sure Jersey's multipart
+ * feature could work in GF with CDI.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends Application {
+
+    static final Set<Class<?>> classes = new HashSet<Class<?>>();
+
+    static {
+        classes.add(EchoResource.class);
+        classes.add(MultiPartFeature.class);
+    }
+    @Override
+    public Set<Class<?>> getClasses() {
+        return classes;
+    }
+}
diff --git a/tests/integration/cdi-multipart-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-multipart-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..aaf0547
--- /dev/null
+++ b/tests/integration/cdi-multipart-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-multipart-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MultipartFeatureTest.java b/tests/integration/cdi-multipart-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MultipartFeatureTest.java
new file mode 100644
index 0000000..5fb1185
--- /dev/null
+++ b/tests/integration/cdi-multipart-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MultipartFeatureTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Part of GF-21033 reproducer. Make sure form data processed by the Jersey multi-part
+ * feature make it forth and back all right.
+ *
+ * <p/>Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-test-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class MultipartFeatureTest extends JerseyTest {
+
+    final String TestFormDATA;
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"No matter what"},
+                {"You should never"},
+                {"ever"},
+                {"just give up"}
+        });
+    }
+
+    public MultipartFeatureTest(String TestFormDATA) {
+        this.TestFormDATA = TestFormDATA;
+    }
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-multipart-webapp").build();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(MultiPartFeature.class);
+    }
+
+    @Test
+    public void testPostFormData() {
+
+        final WebTarget target = target().path("echo");
+
+        FormDataMultiPart formData = new FormDataMultiPart().field("input", TestFormDATA);
+
+        final Response response = target.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA_TYPE));
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(TestFormDATA));
+    }
+}
+
diff --git a/tests/integration/cdi-test-webapp/pom.xml b/tests/integration/cdi-test-webapp/pom.xml
new file mode 100644
index 0000000..bfddb0f
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/pom.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-test-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-webapp</name>
+
+    <description>Jersey CDI test web application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-weld2-se</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>run-external-tests</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <jersey.config.test.container.factory>${external.container.factory}</jersey.config.test.container.factory>
+                                <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- External test container configuration is done via properties to allow overriding via command line. -->
+                <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+                <external.container.port>8080</external.container.port>
+                <maven.test.skip>false</maven.test.skip>
+            </properties>
+        </profile>
+    </profiles>
+</project>
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ConstructorInjectedResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ConstructorInjectedResource.java
new file mode 100644
index 0000000..736dfb9
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ConstructorInjectedResource.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+/**
+ * JERSEY-2526 reproducer. CDI managed JAX-RS root resource
+ * that is constructor injected with JAX-RS parameters provided by Jersey
+ * and a single String parameter coming from application provided CDI producer,
+ * {@link CustomCdiProducer}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("ctor-injected/{p}")
+@RequestScoped
+public class ConstructorInjectedResource {
+
+    String pathParam;
+    String queryParam;
+    String matrixParam;
+    String headerParam;
+    String cdiParam;
+
+    /**
+     * WLS requires this.
+     */
+    public ConstructorInjectedResource() {
+    }
+
+    /**
+     * This will get CDI injected with JAX-RS provided parameters.
+     *
+     * @param pathParam path parameter from the actual request.
+     * @param queryParam query parameter q from the actual request.
+     * @param matrixParam matrix parameter m from the actual request.
+     * @param headerParam Accept header parameter from the actual request.
+     * @param cdiParam custom CDI produced string.
+     */
+    @Inject
+    public ConstructorInjectedResource(
+            @PathParam("p") String pathParam,
+            @QueryParam("q") String queryParam,
+            @MatrixParam("m") String matrixParam,
+            @HeaderParam("Custom-Header") String headerParam,
+            @CustomCdiProducer.Qualifier String cdiParam) {
+
+        this.pathParam = pathParam;
+        this.queryParam = queryParam;
+        this.matrixParam = matrixParam;
+        this.headerParam = headerParam;
+        this.cdiParam = cdiParam;
+    }
+
+    /**
+     * Provide string representation of a single injected parameter
+     * given by the actual path parameter (that is also injected).
+     *
+     * @return a single parameter value.
+     */
+    @GET
+    public String getParameter() {
+
+        switch (pathParam) {
+
+            case "pathParam": return pathParam;
+            case "queryParam": return queryParam;
+            case "matrixParam": return matrixParam;
+            case "headerParam": return headerParam;
+            case "cdiParam": return cdiParam;
+
+            default: throw new WebApplicationException(Response.Status.BAD_REQUEST);
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CounterResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CounterResource.java
new file mode 100644
index 0000000..8342a0c
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CounterResource.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Part of JERSEY-2461 reproducer. This one will get injected with a CDI extension.
+ * HK2 should not mess up with this.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("counter")
+public class CounterResource {
+
+    final CustomExtension e;
+
+    /**
+     * To make CDI happy... namely to make the bean proxy-able.
+     */
+    public CounterResource() {
+        this.e = null;
+    }
+
+    /**
+     * This one will get used at runtime actually.
+     *
+     * @param extension current application CDI custom extension.
+     */
+    @Inject
+    public CounterResource(CustomExtension extension) {
+        this.e = extension;
+    }
+
+    /**
+     * Return custom extension counter state.
+     *
+     * @return next count.
+     */
+    @GET
+    public int getCount() {
+        return e.getCount();
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CustomCdiProducer.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CustomCdiProducer.java
new file mode 100644
index 0000000..9f00838
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CustomCdiProducer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.inject.Produces;
+
+/**
+ * CDI producer to help us make sure HK2 do not mess up with
+ * types backed by CDI producers.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomCdiProducer {
+
+    /**
+     * Custom qualifier to work around https://java.net/jira/browse/GLASSFISH-20285
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
+    @javax.inject.Qualifier
+    public static @interface Qualifier {
+    }
+
+    /**
+     * To cover field producer.
+     */
+    @Produces
+    public static FieldProducedBean<String> field = new FieldProducedBean<>("field");
+
+    /**
+     * To cover method producer.
+     *
+     * @return bean instance to inject
+     */
+    @Produces
+    public MethodProducedBean<String> produceBean() {
+        return new MethodProducedBean<>("method");
+    }
+
+    /**
+     * Part of JERSEY-2526 reproducer. This one is used
+     * to inject constructor of {@link ConstructorInjectedResource}.
+     *
+     * @return fixed string value.
+     */
+    @Produces
+    @Qualifier
+    public String produceString() {
+        return "cdi-produced";
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CustomExtension.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CustomExtension.java
new file mode 100644
index 0000000..081bc51
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CustomExtension.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.enterprise.inject.spi.Extension;
+
+/**
+ * Part of JERSEY-2461 reproducer. We need an extension that we could inject,
+ * to make sure HK2 custom binding does not attempt to mess up.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomExtension implements Extension {
+
+    private AtomicInteger counter = new AtomicInteger();
+
+    /**
+     * A made up functionality. Does not really matter. CDI
+     * would refuse to deploy the application if something went wrong.
+     *
+     * @return next count.
+     */
+    public int getCount() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
new file mode 100644
index 0000000..287d734
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.inject.Qualifier;
+
+/**
+ * Simple echo service to test injections using {@link Qualifier}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface EchoService {
+
+    /**
+     * Provide an echoed response.
+     *
+     * @param s String to be echoed.
+     * @return echoed input.
+     */
+    public String echo(String s);
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/FieldProducedBean.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/FieldProducedBean.java
new file mode 100644
index 0000000..2dbb6c4
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/FieldProducedBean.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.inject.Vetoed;
+
+/**
+ * A bean that would be produced by a CDI producer field.
+ * This is to make sure we do not mess up with CDI producers with HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Vetoed
+public class FieldProducedBean<T> implements ValueHolder<T> {
+
+    private final T value;
+
+    /**
+     * Make an instance with given value.
+     *
+     * @param value
+     */
+    public FieldProducedBean(T value) {
+        this.value = value;
+    }
+
+    @Override
+    public T getValue() {
+        return value;
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/FirstNonJaxRsBeanInjectedResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/FirstNonJaxRsBeanInjectedResource.java
new file mode 100644
index 0000000..60b552e
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/FirstNonJaxRsBeanInjectedResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * JAX-RS resource that gets injected with a CDI managed bean,
+ * that includes JAX-RS injection points. The very same bean
+ * gets injected also to {@link FirstNonJaxRsBeanInjectedResource}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("non-jaxrs-bean-injected")
+@RequestScoped
+public class FirstNonJaxRsBeanInjectedResource {
+
+    @Inject
+    JaxRsInjectedBean bean;
+
+    @GET
+    @Path("path/1")
+    public String getPath() {
+        return bean.getUriInfo().getPath();
+    }
+
+    @GET
+    @Path("header/1")
+    public String getAcceptHeader() {
+        return bean.getTestHeader();
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentExceptionMapper.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentExceptionMapper.java
new file mode 100644
index 0000000..390dc97
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentExceptionMapper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.PostConstruct;
+
+/**
+ * JAX-RS exception mapper registered as a CDI managed bean.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@ManagedBean
+public class JCDIBeanDependentExceptionMapper implements ExceptionMapper<JDCIBeanDependentException> {
+
+    private static final Logger LOGGER = Logger.getLogger(JCDIBeanDependentExceptionMapper.class.getName());
+
+    @Context
+    private UriInfo uiFieldInject;
+
+    @Context
+    private ResourceContext resourceContext;
+
+    private UriInfo uiMethodInject;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+        LOGGER.log(Level.INFO, String.format("In post construct of %s", this));
+        ensureInjected();
+    }
+
+    @Override
+    public Response toResponse(JDCIBeanDependentException exception) {
+        ensureInjected();
+        return Response.serverError().entity("JDCIBeanDependentException").build();
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldInject == null || uiMethodInject == null || resourceContext == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentPerRequestResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentPerRequestResource.java
new file mode 100644
index 0000000..2a36eff
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentPerRequestResource.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.Resource;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.RequestScoped;
+
+
+/**
+ * Request scoped JAX-RS resource registered as a CDI managed bean.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/jcdibean/dependent/per-request")
+@ManagedBean
+@RequestScoped
+public class JCDIBeanDependentPerRequestResource {
+
+    private static final Logger LOGGER = Logger.getLogger(JCDIBeanDependentPerRequestResource.class.getName());
+
+    @Resource(name = "injectedResource")
+    private int injectedResource = 0;
+
+    @Context
+    private UriInfo uiFieldInject;
+
+    @Context
+    private ResourceContext rc;
+
+    @QueryParam("x")
+    private String x;
+
+    private UriInfo uiMethodInject;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+
+        ensureInjected();
+
+        LOGGER.info(String.format(
+                "In post construct of %s; uiFieldInject: %s; uiMethodInject: %s",
+                this, uiFieldInject, uiMethodInject));
+    }
+
+    @GET
+    @Produces("text/plain")
+    public String getMessage() {
+
+        ensureInjected();
+
+        LOGGER.info(String.format(
+                "In getMessage of %s; uiFieldInject: %s; uiMethodInject: %s",
+                this, uiFieldInject, uiMethodInject));
+
+        return String.format("%s: queryParam=%s %d", uiFieldInject.getRequestUri().toString(), x, injectedResource++);
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        LOGGER.info(String.format("In pre destroy of %s", this));
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldInject == null || uiMethodInject == null || rc == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentResource.java
new file mode 100644
index 0000000..589de49
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test case for JERSEY-1747.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/jcdibean/dependent/timer")
+public class JCDIBeanDependentResource {
+
+   @Inject
+   JCDIBeanRequestScopedTimer timer;
+
+   @GET
+   @Produces("text/plain")
+   public String getValue() {
+       return Long.toString(timer.getMiliseconds());
+   }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentSingletonResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentSingletonResource.java
new file mode 100644
index 0000000..09ecf81
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanDependentSingletonResource.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.ManagedBean;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Application scoped JAX-RS resource registered as CDI managed bean.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/jcdibean/dependent/singleton/{p}")
+@ApplicationScoped
+@ManagedBean
+public class JCDIBeanDependentSingletonResource {
+
+    private static final Logger LOGGER = Logger.getLogger(JCDIBeanDependentSingletonResource.class.getName());
+
+    @Resource(name = "injectedResource")
+    private int counter = 0;
+
+    @Context
+    private UriInfo uiFieldinject;
+
+    @Context
+    private ResourceContext resourceContext;
+
+    private UriInfo uiMethodInject;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+        LOGGER.info(String.format("In post construct of %s", this));
+        ensureInjected();
+    }
+
+    @GET
+    @Produces("text/plain")
+    public String getMessage(@PathParam("p") String p) {
+        LOGGER.info(String.format(
+                "In getMessage in %s; uiFieldInject: %s; uiMethodInject: %s", this, uiFieldinject, uiMethodInject));
+        ensureInjected();
+
+        return String.format("%s: p=%s, queryParam=%s",
+                uiFieldinject.getRequestUri().toString(), p, uiMethodInject.getQueryParameters().getFirst("x"));
+    }
+
+    @Path("exception")
+    public String getException() {
+        throw new JDCIBeanDependentException();
+    }
+
+    @Path("counter")
+    @GET
+    public synchronized String getCounter() {
+        return Integer.toString(counter++);
+    }
+
+    @Path("counter")
+    @PUT
+    public synchronized void setCounter(String counter) {
+        this.counter = Integer.decode(counter);
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        LOGGER.info(String.format("In pre destroy of %s", this));
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldinject == null || uiMethodInject == null || resourceContext == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanExceptionMapper.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanExceptionMapper.java
new file mode 100644
index 0000000..d6a06ed
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanExceptionMapper.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * JAX-RS exception mapper registered as CDI managed bean.
+ *
+ * @author Paul Sandoz
+ */
+@Provider
+@ApplicationScoped
+public class JCDIBeanExceptionMapper implements ExceptionMapper<JDCIBeanException> {
+
+    private static final Logger LOGGER = Logger.getLogger(JCDIBeanExceptionMapper.class.getName());
+
+    @Context
+    private UriInfo uiFieldInject;
+
+    @Context
+    private ResourceContext rc;
+
+    private UriInfo uiMethodInject;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+        ensureInjected();
+        LOGGER.info(String.format("In post construct of %s", this));
+    }
+
+    @Override
+    public Response toResponse(JDCIBeanException exception) {
+        ensureInjected();
+        return Response.serverError().entity("JDCIBeanException").build();
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldInject == null || uiMethodInject == null || rc == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanPerRequestResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanPerRequestResource.java
new file mode 100644
index 0000000..303e9ad
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanPerRequestResource.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.logging.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import javax.enterprise.context.RequestScoped;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Request scoped JAX-RS resource registered.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/jcdibean/per-request")
+@RequestScoped
+public class JCDIBeanPerRequestResource {
+
+    private static final Logger LOGGER = Logger.getLogger(JCDIBeanPerRequestResource.class.getName());
+
+    @Resource(name = "injectedResource")
+    private int injectedResource = 0;
+
+    @Context
+    private UriInfo uiFieldInject;
+
+    @Context
+    private ResourceContext rc;
+
+    @QueryParam("x")
+    private String x;
+
+    private UriInfo uiMethodInject;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+
+        ensureInjected();
+
+        LOGGER.info(String.format(
+                "In post construct of %s; uiFieldInject: %s; uiMethodInject: %s",
+                this, uiFieldInject, uiMethodInject));
+    }
+
+    @GET
+    @Produces("text/plain")
+    public String getMessage() {
+
+        ensureInjected();
+
+        LOGGER.info(String.format(
+                "In getMessage of %s; uiFieldInject: %s; uiMethodInject: %s",
+                this, uiFieldInject, uiMethodInject));
+
+        return String.format("%s: queryParam=%s %d", uiFieldInject.getRequestUri().toString(), x, injectedResource++);
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        LOGGER.info(String.format("In pre destroy of %s", this));
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldInject == null || uiMethodInject == null || rc == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanRequestScopedTimer.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanRequestScopedTimer.java
new file mode 100644
index 0000000..019f5a5
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanRequestScopedTimer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Request scoped CDI bean to be injected into {@link JCDIBeanDependentResource}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+public class JCDIBeanRequestScopedTimer {
+
+   private long ms;
+
+   /**
+    * Provide information on actual request time.
+    *
+    * @return milliseconds when the current bean was post-constructed.
+    */
+   public long getMiliseconds() {
+       return ms;
+   }
+
+   @PostConstruct
+   public void init() {
+       this.ms = System.currentTimeMillis();
+   }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanSingletonResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanSingletonResource.java
new file mode 100644
index 0000000..a181f4f
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JCDIBeanSingletonResource.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/**
+ * Application scoped JAX-RS resource.
+ *
+ * @author Paul Sandoz
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/jcdibean/singleton/{p}")
+@ApplicationScoped
+public class JCDIBeanSingletonResource {
+
+    private static final Logger LOGGER = Logger.getLogger(JCDIBeanSingletonResource.class.getName());
+
+    @Resource(name = "injectedResource")
+    private int counter = 0;
+
+    @Context
+    private UriInfo uiFieldinject;
+
+    @Context
+    private
+    ResourceContext resourceContext;
+
+    private UriInfo uiMethodInject;
+
+    @Inject
+    Provider<JCDIBeanExceptionMapper> mapperProvider;
+
+    @Context
+    public void set(UriInfo ui) {
+        this.uiMethodInject = ui;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+        LOGGER.info(String.format("In post construct of %s", this));
+        ensureInjected();
+    }
+
+    @GET
+    @Produces("text/plain")
+    public String getMessage(@PathParam("p") String p) {
+        LOGGER.info(String.format(
+                "In getMessage in %s; uiFieldInject: %s; uiMethodInject: %s; provider: %s; provider.get(): %s", this,
+                uiFieldinject, uiMethodInject, mapperProvider, mapperProvider.get()));
+        ensureInjected();
+
+        return String.format("%s: p=%s, queryParam=%s",
+                uiFieldinject.getRequestUri().toString(), p, uiMethodInject.getQueryParameters().getFirst("x"));
+    }
+
+    @Path("exception")
+    public String getException() {
+        throw new JDCIBeanException();
+    }
+
+    @Path("counter")
+    @GET
+    public synchronized String getCounter() {
+        return Integer.toString(counter++);
+    }
+
+    @Path("counter")
+    @PUT
+    public synchronized void setCounter(String counter) {
+        this.counter = Integer.decode(counter);
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        LOGGER.info(String.format("In pre destroy of %s", this));
+    }
+
+    private void ensureInjected() throws IllegalStateException {
+        if (uiFieldinject == null || uiMethodInject == null
+                || resourceContext == null || mapperProvider.get() == null) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JDCIBeanDependentException.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JDCIBeanDependentException.java
new file mode 100644
index 0000000..73e837a
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JDCIBeanDependentException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+/**
+ * Exception to be treated by {@link JCDIBeanDependentExceptionMapper}.
+ *
+ * @author Paul Sandoz
+ */
+public class JDCIBeanDependentException extends RuntimeException {
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JDCIBeanException.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JDCIBeanException.java
new file mode 100644
index 0000000..2b80a0c
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JDCIBeanException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+/**
+ * Exception to be treated by {@link JCDIBeanExceptionMapper}.
+ *
+ * @author Paul Sandoz
+ */
+public class JDCIBeanException extends RuntimeException {
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JaxRsInjectedBean.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JaxRsInjectedBean.java
new file mode 100644
index 0000000..fd7a884
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/JaxRsInjectedBean.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * This is to be JAX-RS injected at runtime.
+ * The bean is fully CDI managed, contains JAX-RS injection points and is getting injected into JAX-RS
+ * resources included in two distinct JAX-RS applications running in parallel.
+ */
+@RequestScoped
+public class JaxRsInjectedBean {
+
+    @HeaderParam("x-test")
+    String testHeader;
+
+    @Context
+    UriInfo uriInfo;
+
+    public UriInfo getUriInfo() {
+        return uriInfo;
+    }
+
+    public String getTestHeader() {
+        return testHeader;
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
new file mode 100644
index 0000000..b6ec0e6
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+
+import javax.inject.Inject;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jonathan Benoit (jonathan.benoit at oracle.com)
+ */
+@ApplicationPath("main")
+@ApplicationScoped
+public class MainApplication extends Application {
+
+    static AtomicInteger postConstructCounter = new AtomicInteger();
+
+    @Inject BeanManager bm;
+
+    private static final Logger LOGGER = Logger.getLogger(MainApplication.class.getName());
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(JCDIBeanDependentResource.class);
+        classes.add(JDCIBeanException.class);
+        classes.add(JDCIBeanDependentException.class);
+        classes.add(JCDIBeanSingletonResource.class);
+        classes.add(JCDIBeanPerRequestResource.class);
+        classes.add(JCDIBeanExceptionMapper.class);
+        classes.add(JCDIBeanDependentSingletonResource.class);
+        classes.add(JCDIBeanDependentPerRequestResource.class);
+        classes.add(JCDIBeanDependentExceptionMapper.class);
+        classes.add(StutteringEchoResource.class);
+        classes.add(StutteringEcho.class);
+        classes.add(ReversingEchoResource.class);
+        classes.add(CounterResource.class);
+        classes.add(ConstructorInjectedResource.class);
+        classes.add(ProducerResource.class);
+        classes.add(FirstNonJaxRsBeanInjectedResource.class);
+        return classes;
+    }
+
+    // JERSEY-2531: make sure this type gets managed by CDI
+    @PostConstruct
+    public void postConstruct() {
+        LOGGER.info(String.format("%s: POST CONSTRUCT.", this.getClass().getName()));
+        postConstructCounter.incrementAndGet();
+        if (bm == null) {
+            throw new IllegalStateException("BeanManager should have been injected into a CDI managed bean.");
+        }
+        if (postConstructCounter.intValue() > 1) {
+            throw new IllegalStateException("postConstruct should have been invoked only once on app scoped bean.");
+        }
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        LOGGER.info(String.format("%s: PRE DESTROY.", this.getClass().getName()));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MethodProducedBean.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MethodProducedBean.java
new file mode 100644
index 0000000..94f842f
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MethodProducedBean.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.inject.Vetoed;
+
+/**
+ * A bean that would be produced by a CDI producer method.
+ * This is to make sure we do not mess up with CDI producers with HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Vetoed
+public class MethodProducedBean<T> implements ValueHolder<T> {
+
+    private final T value;
+
+    /**
+     * Make an instance with given value.
+     *
+     * @param value
+     */
+    public MethodProducedBean(T value) {
+        this.value = value;
+    }
+
+    /**
+     * Value getter.
+     *
+     * @return value.
+     */
+    @Override
+    public T getValue() {
+        return value;
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ProducerResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ProducerResource.java
new file mode 100644
index 0000000..4588692
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ProducerResource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * This one will get injected with a CDI producer.
+ * HK2 should not mess up with this.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("producer")
+public class ProducerResource {
+
+    @Inject
+    MethodProducedBean<String> m;
+
+    @Inject
+    FieldProducedBean<String> f;
+
+    /**
+     * Return field produced bean value.
+     *
+     * @return value from field produced bean.
+     */
+    @Path("f")
+    @GET
+    public String getFieldValue() {
+        return f.getValue();
+    }
+
+    /**
+     * Return method produced bean value.
+     *
+     * @return value from method produced bean.
+     */
+    @Path("m")
+    @GET
+    public String getMethodValue() {
+        return m.getValue();
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ReverseEcho.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ReverseEcho.java
new file mode 100644
index 0000000..b385655
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ReverseEcho.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+/**
+ * Echo implementation to reverse given input.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Reversing
+public class ReverseEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return reverseString(s);
+    }
+
+    private String reverseString(final String s) {
+        final int len = s.length();
+
+        char[] chars = new char[len];
+        for (int i = 0; i < len; i++) {
+            chars[i] = s.charAt(len - i - 1);
+        }
+        return new String(chars);
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Reversing.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Reversing.java
new file mode 100644
index 0000000..afe0ff5
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Reversing.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for reversing echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface Reversing {
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ReversingEchoResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ReversingEchoResource.java
new file mode 100644
index 0000000..c1eef59
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ReversingEchoResource.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ * CDI backed JAX-RS resource to reverse input query parameter using
+ * qualified injection to get a CDI backed service provider.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("reverse")
+public class ReversingEchoResource {
+
+    @Inject @Reversing EchoService echoService;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondNonJaxRsBeanInjectedResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondNonJaxRsBeanInjectedResource.java
new file mode 100644
index 0000000..eb4650a
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondNonJaxRsBeanInjectedResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.resources;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * JAX-RS resource that gets injected with a CDI managed bean,
+ * that includes JAX-RS injection points. The very same bean
+ * gets injected also to {@link FirstNonJaxRsBeanInjectedResource}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("non-jaxrs-bean-injected")
+@RequestScoped
+public class SecondNonJaxRsBeanInjectedResource {
+
+    @Inject
+    JaxRsInjectedBean bean;
+
+    @GET
+    @Path("path/2")
+    public String getPath() {
+        return bean.getUriInfo().getPath();
+    }
+
+    @GET
+    @Path("header/2")
+    public String getAcceptHeader() {
+        return bean.getTestHeader();
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java
new file mode 100644
index 0000000..fccc031
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010, 2018 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.cdi.resources;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Secondary JAX-RS application to configure resources.
+ * This one is to make sure two Jersey apps could co-exist
+ * in CDI managed environment and JAX-RS injection keeps
+ * functioning as expected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("secondary")
+@ApplicationScoped
+public class SecondaryApplication extends Application {
+
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(SecondNonJaxRsBeanInjectedResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Stuttering.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Stuttering.java
new file mode 100644
index 0000000..a1cc1a8
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Stuttering.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for stuttering echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface Stuttering {
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/StutteringEcho.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/StutteringEcho.java
new file mode 100644
index 0000000..0579f8c
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/StutteringEcho.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.enterprise.context.ApplicationScoped;
+
+/**
+ * Echo implementation to stutter given input n-times.
+ * The stutter factor could be set via JAX-RS interface.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stuttering
+@ApplicationScoped
+@Path("stutter-service-factor")
+public class StutteringEcho implements EchoService {
+
+    private static final int MIN_FACTOR = 2;
+    private int factor = MIN_FACTOR;
+
+    @Override
+    public String echo(String s) {
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < factor; i++) {
+            result.append(s);
+        }
+        return result.toString();
+    }
+
+    @PUT
+    public void setFactor(String factor) {
+        this.factor = ensureValidInput(factor);
+    }
+
+    @GET
+    public String getFactor() {
+        return Integer.toString(factor);
+    }
+
+    private int ensureValidInput(String factor) throws WebApplicationException {
+        try {
+            final int newValue = Integer.parseInt(factor);
+            if (newValue < MIN_FACTOR) {
+                throw createWebAppException(String.format("New factor can not be lesser then %d!", MIN_FACTOR));
+            }
+            return newValue;
+        } catch (NumberFormatException nfe) {
+            throw createWebAppException(String.format("Error parsing %s as an integer!", factor));
+        }
+    }
+
+    private WebApplicationException createWebAppException(String message) {
+
+        return new WebApplicationException(
+
+                Response.status(Response.Status.BAD_REQUEST)
+                        .type(MediaType.TEXT_PLAIN)
+                        .entity(Entity.text(message)).build());
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/StutteringEchoResource.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/StutteringEchoResource.java
new file mode 100644
index 0000000..c3d5a56
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/StutteringEchoResource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * CDI backed JAX-RS resource to stutter input query parameter.
+ * Uses qualified injection to get a CDI backed service provider.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("stutter")
+public class StutteringEchoResource {
+
+    @Inject @Stuttering EchoService echoService;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ValueHolder.java b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ValueHolder.java
new file mode 100644
index 0000000..11046fc
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/ValueHolder.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * Helper type to check CDI producer mechanism is not broken
+ * by automatic HK2/CDI bindings.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface ValueHolder<T> {
+
+    /**
+     * Value getter.
+     *
+     * @return value.
+     */
+    T getValue();
+}
diff --git a/tests/integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml b/tests/integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-test-webapp/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/tests/integration/cdi-test-webapp/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..7331615
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.cdi.resources.CustomExtension
diff --git a/tests/integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-test-webapp/src/main/webapp/WEB-INF/web.xml b/tests/integration/cdi-test-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ff67ca5
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+    <env-entry>
+        <env-entry-name>injectedResource</env-entry-name>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+        <env-entry-value>10</env-entry-value>
+    </env-entry>
+</web-app>
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
new file mode 100644
index 0000000..a3137a9
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.jboss.weld.environment.se.Weld;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-test-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiTest extends JerseyTest {
+
+    Weld weld;
+
+    @Override
+    public void setUp() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        weld.shutdown();
+        super.tearDown();
+    }
+
+    @Override
+    protected Application configure() {
+        return new MainApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-test-webapp/main").build();
+    }
+}
+
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ConstructorInjectionTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ConstructorInjectionTest.java
new file mode 100644
index 0000000..0263084
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ConstructorInjectionTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+/**
+ * Part of JERSEY-2526 reproducer. Without the fix, the application would
+ * not deploy at all. This is just to make sure the JAX-RS parameter producer
+ * keeps working as expected without regressions.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oralc.com)
+ */
+public class ConstructorInjectionTest extends CdiTest {
+
+    @Test
+    public void testConstructorInjectedResource() {
+
+        final WebTarget target = target().path("ctor-injected");
+
+        final Response pathParamResponse = target.path("pathParam").request().get();
+        assertThat(pathParamResponse.getStatus(), is(200));
+        assertThat(pathParamResponse.readEntity(String.class), is("pathParam"));
+
+        final Response queryParamResponse = target.path("queryParam").queryParam("q", "123").request().get();
+        assertThat(queryParamResponse.getStatus(), is(200));
+        assertThat(queryParamResponse.readEntity(String.class), is("123"));
+
+        final Response matrixParamResponse = target.path("matrixParam").matrixParam("m", "456").request().get();
+        assertThat(matrixParamResponse.getStatus(), is(200));
+        assertThat(matrixParamResponse.readEntity(String.class), is("456"));
+
+        final Response headerParamResponse = target.path("headerParam").request().header("Custom-Header", "789").get();
+        assertThat(headerParamResponse.getStatus(), is(200));
+        assertThat(headerParamResponse.readEntity(String.class), is("789"));
+
+        final Response cdiParamResponse = target.path("cdiParam").request().get();
+        assertThat(cdiParamResponse.getStatus(), is(200));
+        assertThat(cdiParamResponse.readEntity(String.class), is("cdi-produced"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CounterTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CounterTest.java
new file mode 100644
index 0000000..62f4f5d
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CounterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.is;
+
+/**
+ * Part of JERSEY-2641 reproducer. Accessing CDI bean that has custom CDI
+ * extension injected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CounterTest extends CdiTest {
+
+    @Test
+    public void testGet() {
+
+        final WebTarget target = target().path("counter");
+
+        final Response firstResponse = target.request().get();
+        assertThat(firstResponse.getStatus(), is(200));
+        int firstNumber = Integer.decode(firstResponse.readEntity(String.class));
+
+        final Response secondResponse = target.request().get();
+        assertThat(secondResponse.getStatus(), is(200));
+        int secondNumber = Integer.decode(secondResponse.readEntity(String.class));
+
+        assertTrue("Second request should have greater number!", secondNumber > firstNumber);
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/JaxRsInjectedCdiBeanTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/JaxRsInjectedCdiBeanTest.java
new file mode 100644
index 0000000..56cc87b
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/JaxRsInjectedCdiBeanTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.resources;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test that a raw CDI managed bean gets JAX-RS injected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsInjectedCdiBeanTest extends CdiTest {
+
+    @Test
+    public void testPathAndHeader() {
+        _testPathAndHeader(target());
+    }
+
+    public static void _testPathAndHeader(final WebTarget webTarget) {
+        final WebTarget target = webTarget.path("non-jaxrs-bean-injected");
+
+        final Response pathResponse = target.path("path/1").request().get();
+        assertThat(pathResponse.getStatus(), is(200));
+        final String path = pathResponse.readEntity(String.class);
+
+        assertThat(path, is("non-jaxrs-bean-injected/path/1"));
+
+        final Response headerResponse = target.path("header/1").request().header("x-test", "bummer").get();
+        assertThat(headerResponse.getStatus(), is(200));
+        final String header = headerResponse.readEntity(String.class);
+
+        assertThat(header, is("bummer"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/NonJaxRsBeanJaxRsInjectionTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/NonJaxRsBeanJaxRsInjectionTest.java
new file mode 100644
index 0000000..2775ebf
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/NonJaxRsBeanJaxRsInjectionTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.resources;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Properties;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainerProvider;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.hamcrest.CoreMatchers;
+import org.jboss.weld.environment.se.Weld;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test two Jersey apps running simultaneously within a single Grizzly HTTP server
+ * to make sure two injection managers do not interfere. The test is not executed
+ * if other than the default (Grizzly) test container has been set.
+ * For Servlet based container testing, the other two tests, {@link JaxRsInjectedCdiBeanTest}
+ * and {@link SecondJaxRsInjectedCdiBeanTest},
+ * do the same job, because the WAR application contains both Jersey apps already.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class NonJaxRsBeanJaxRsInjectionTest {
+
+    public static final String MAIN_URI = "/main";
+    public static final String SECONDARY_URI = "/secondary";
+
+    public static final String PORT_NUMBER = getSystemProperty(TestProperties.CONTAINER_PORT,
+                                                    Integer.toString(TestProperties.DEFAULT_CONTAINER_PORT));
+
+    private static final URI MAIN_APP_URI = URI.create("http://localhost:" + PORT_NUMBER + MAIN_URI);
+    private static final URI SECONDARY_APP_URI = URI.create("http://localhost:" + PORT_NUMBER + SECONDARY_URI);
+
+    private static final boolean isDefaultTestContainerFactorySet = isDefaultTestContainerFactorySet();
+
+    Weld weld;
+    HttpServer httpServer;
+
+    Client client;
+    WebTarget mainTarget, secondaryTarget;
+
+    @Before
+    public void before() throws IOException {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+
+        if (isDefaultTestContainerFactorySet) {
+            initializeWeld();
+            startGrizzlyContainer();
+            initializeClient();
+        }
+    }
+
+    @After
+    public void after() {
+        if (Hk2InjectionManagerFactory.isImmediateStrategy()) {
+            if (isDefaultTestContainerFactorySet) {
+                httpServer.shutdownNow();
+                weld.shutdown();
+                client.close();
+            }
+        }
+    }
+
+    @Test
+    public void testPathAndHeader() throws Exception {
+        Assume.assumeThat(isDefaultTestContainerFactorySet, CoreMatchers.is(true));
+        JaxRsInjectedCdiBeanTest._testPathAndHeader(mainTarget);
+        SecondJaxRsInjectedCdiBeanTest._testPathAndHeader(secondaryTarget);
+    }
+
+    private void initializeWeld() {
+        weld = new Weld();
+        weld.initialize();
+    }
+
+    private void startGrizzlyContainer() throws IOException {
+        final ResourceConfig firstConfig = ResourceConfig.forApplicationClass(MainApplication.class);
+        final ResourceConfig secondConfig = ResourceConfig.forApplicationClass(SecondaryApplication.class);
+
+        httpServer = GrizzlyHttpServerFactory.createHttpServer(MAIN_APP_URI, firstConfig, false);
+        final HttpHandler secondHandler = createGrizzlyContainer(secondConfig);
+        httpServer.getServerConfiguration().addHttpHandler(secondHandler, SECONDARY_URI);
+        httpServer.start();
+    }
+
+    private GrizzlyHttpContainer createGrizzlyContainer(final ResourceConfig resourceConfig) {
+        return (new GrizzlyHttpContainerProvider()).createContainer(GrizzlyHttpContainer.class, resourceConfig);
+    }
+
+    private void initializeClient() {
+        client = ClientBuilder.newClient();
+        mainTarget = client.target(MAIN_APP_URI);
+        secondaryTarget = client.target(SECONDARY_APP_URI);
+    }
+
+    private static boolean isDefaultTestContainerFactorySet() {
+        final String testContainerFactory = getSystemProperty(TestProperties.CONTAINER_FACTORY, null);
+        return testContainerFactory == null || TestProperties.DEFAULT_CONTAINER_FACTORY.equals(testContainerFactory);
+    }
+
+    private static String getSystemProperty(final String propertyName, final String defaultValue) {
+        final Properties systemProperties = System.getProperties();
+        return systemProperties.getProperty(propertyName, defaultValue);
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/PerRequestBeanTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/PerRequestBeanTest.java
new file mode 100644
index 0000000..19d3984
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/PerRequestBeanTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the request scoped resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class PerRequestBeanTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha"},
+                {"AAA"},
+                {"$%^"},
+                {"a b"}
+        });
+    }
+
+    final String x;
+
+    /**
+     * Create x new test case based on the above defined parameters.
+     *
+     * @param x query parameter value
+     */
+    public PerRequestBeanTest(String x) {
+        this.x = x;
+    }
+
+    @Test
+    public void testGet() {
+
+        final WebTarget target = target().path("jcdibean/per-request").queryParam("x", x);
+
+        String s = target.request().get(String.class);
+
+        assertThat(s, containsString(target.getUri().toString()));
+        assertThat(s, containsString(String.format("queryParam=%s", x)));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/PerRequestDependentBeanTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/PerRequestDependentBeanTest.java
new file mode 100644
index 0000000..dd2e879
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/PerRequestDependentBeanTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the request scoped managed bean resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class PerRequestDependentBeanTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha"},
+                {"AAA"},
+                {"$%^"},
+                {"a b"}
+        });
+    }
+
+    final String x;
+
+    /**
+     * Create x new test case based on the above defined parameters.
+     *
+     * @param x query parameter value
+     */
+    public PerRequestDependentBeanTest(String x) {
+        this.x = x;
+    }
+
+    @Test
+    public void testGet() {
+
+        final WebTarget target = target().path("jcdibean/dependent/per-request").queryParam("x", x);
+
+        String s = target.request().get(String.class);
+
+        assertThat(s, containsString(target.getUri().toString()));
+        assertThat(s, containsString(String.format("queryParam=%s", x)));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ProducerTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ProducerTest.java
new file mode 100644
index 0000000..1ecf8cd
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ProducerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+
+/**
+ * Check that automatic HK2 bindings do not break CDI producer mechanism.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ProducerTest extends CdiTest {
+
+    @Test
+    public void testGet() {
+
+        final WebTarget target = target().path("producer");
+
+        final Response fieldResponse = target.path("f").request().get();
+        assertThat(fieldResponse.getStatus(), is(200));
+        assertThat(fieldResponse.readEntity(String.class), is("field"));
+
+        final Response methodResponse = target.path("m").request().get();
+        assertThat(methodResponse.getStatus(), is(200));
+        assertThat(methodResponse.readEntity(String.class), is("method"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/QualifiedInjectionSetGetTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/QualifiedInjectionSetGetTest.java
new file mode 100644
index 0000000..9a01764
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/QualifiedInjectionSetGetTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import javax.inject.Qualifier;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+
+/**
+ * Test CDI bean injected using a {@link Qualifier}
+ * is setup via JAX-RS interface first.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class QualifiedInjectionSetGetTest extends CdiTest {
+
+    @Test
+    public void testSetGet() {
+
+        final WebTarget factorTarget = target().path("stutter-service-factor");
+        final WebTarget stutterTarget = target().path("stutter");
+
+        factorTarget.request().put(Entity.text("3"));
+        final String shouldBeTrippled = stutterTarget.queryParam("s", "lincoln").request().get(String.class);
+
+        assertThat(shouldBeTrippled, is("lincolnlincolnlincoln"));
+
+        factorTarget.request().put(Entity.text("2"));
+        final String shouldBeDoubled = stutterTarget.queryParam("s", "lincoln").request().get(String.class);
+
+        assertThat(shouldBeDoubled, is("lincolnlincoln"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ReverseEchoTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ReverseEchoTest.java
new file mode 100644
index 0000000..0c3c5ca
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ReverseEchoTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for qualified injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ReverseEchoTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha", "ahpla"},
+                {"gogol", "logog"},
+                {"elcaro", "oracle"}
+        });
+    }
+
+    final String in, out;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param in query parameter.
+     * @param out expected output.
+     */
+    public ReverseEchoTest(String in, String out) {
+        this.in = in;
+        this.out = out;
+    }
+
+    @Test
+    public void testGet() {
+        WebTarget reverseService = target().path("reverse").queryParam("s", in);
+        String s = reverseService.request().get(String.class);
+        assertThat(s, equalTo(out));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SecondJaxRsInjectedCdiBeanTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SecondJaxRsInjectedCdiBeanTest.java
new file mode 100644
index 0000000..a273de4
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SecondJaxRsInjectedCdiBeanTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, 2018 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.cdi.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.jboss.weld.environment.se.Weld;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test that a raw CDI managed bean gets JAX-RS injected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class SecondJaxRsInjectedCdiBeanTest extends JerseyTest {
+    Weld weld;
+
+    @Override
+    public void setUp() throws Exception {
+        weld = new Weld();
+        weld.initialize();
+        super.setUp();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        weld.shutdown();
+        super.tearDown();
+    }
+
+    @Override
+    protected Application configure() {
+        return new SecondaryApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-test-webapp/secondary").build();
+    }
+
+    @Test
+    public void testPathAndHeader() {
+        _testPathAndHeader(target());
+    }
+
+    public static void _testPathAndHeader(final WebTarget webTarget) {
+        final WebTarget target = webTarget.path("non-jaxrs-bean-injected");
+
+        final Response pathResponse = target.path("path/2").request().get();
+        assertThat(pathResponse.getStatus(), is(200));
+        final String path = pathResponse.readEntity(String.class);
+
+        assertThat(path, is("non-jaxrs-bean-injected/path/2"));
+
+        final Response headerResponse = target.path("header/2").request().header("x-test", "bummer2").get();
+        assertThat(headerResponse.getStatus(), is(200));
+        final String header = headerResponse.readEntity(String.class);
+
+        assertThat(header, is("bummer2"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SingletonBeanTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SingletonBeanTest.java
new file mode 100644
index 0000000..0b35e30
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SingletonBeanTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the application scoped resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class SingletonBeanTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha", "beta"},
+                {"1", "2"}
+        });
+    }
+
+    final String p, x;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param p path parameter.
+     * @param x query parameter.
+     */
+    public SingletonBeanTest(String p, String x) {
+        this.p = p;
+        this.x = x;
+    }
+
+    @Test
+    public void testGet() {
+        final WebTarget singleton = target().path("jcdibean/singleton").path(p).queryParam("x", x);
+        String s = singleton.request().get(String.class);
+        assertThat(s, containsString(singleton.getUri().toString()));
+        assertThat(s, containsString(String.format("p=%s", p)));
+        assertThat(s, containsString(String.format("queryParam=%s", x)));
+    }
+
+    @Test
+    public void testCounter() {
+
+        final WebTarget counter = target().path("jcdibean/singleton").path(p).queryParam("x", x).path("counter");
+
+        if (!ExternalTestContainerFactory.class.isAssignableFrom(getTestContainerFactory().getClass())) {
+             // TODO: remove this workaround once JERSEY-2744 is resolved
+            counter.request().put(Entity.text("10"));
+        }
+
+        String c10 = counter.request().get(String.class);
+        assertThat(c10, containsString("10"));
+
+        String c11 = counter.request().get(String.class);
+        assertThat(c11, containsString("11"));
+
+        counter.request().put(Entity.text("32"));
+
+        String c32 = counter.request().get(String.class);
+        assertThat(c32, containsString("32"));
+
+        counter.request().put(Entity.text("10"));
+    }
+
+    @Test
+    public void testException() {
+        final WebTarget exception = target().path("jcdibean/singleton").path(p).queryParam("x", x).path("exception");
+        assertThat(exception.request().get().readEntity(String.class), containsString("JDCIBeanException"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SingletonDependentBeanTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SingletonDependentBeanTest.java
new file mode 100644
index 0000000..5a21ae8
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/SingletonDependentBeanTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for the application scoped managed bean resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class SingletonDependentBeanTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"alpha", "beta"},
+                {"1", "2"}
+        });
+    }
+
+    final String p, x;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param p path parameter.
+     * @param x query parameter.
+     */
+    public SingletonDependentBeanTest(String p, String x) {
+        this.p = p;
+        this.x = x;
+    }
+
+    @Test
+    public void testGet() {
+        final WebTarget singleton = target().path("jcdibean/dependent/singleton").path(p).queryParam("x", x);
+        String s = singleton.request().get(String.class);
+        assertThat(s, containsString(singleton.getUri().toString()));
+        assertThat(s, containsString(String.format("p=%s", p)));
+        assertThat(s, containsString(String.format("queryParam=%s", x)));
+    }
+
+    @Test
+    public void testCounter() {
+
+        final WebTarget counter = target().path("jcdibean/dependent/singleton").path(p).queryParam("x", x).path("counter");
+
+        if (!ExternalTestContainerFactory.class.isAssignableFrom(getTestContainerFactory().getClass())) {
+            // TODO: remove this workaround once JERSEY-2744 is resolved
+            counter.request().put(Entity.text("10"));
+        }
+
+        String c10 = counter.request().get(String.class);
+        assertThat(c10, containsString("10"));
+
+        String c11 = counter.request().get(String.class);
+        assertThat(c11, containsString("11"));
+
+        counter.request().put(Entity.text("32"));
+
+        String c32 = counter.request().get(String.class);
+        assertThat(c32, containsString("32"));
+
+        counter.request().put(Entity.text("10"));
+    }
+
+    @Test
+    public void testException() {
+        final WebTarget exception = target().path("jcdibean/dependent/singleton").path(p).queryParam("x", x).path("exception");
+        assertThat(exception.request().get().readEntity(String.class), containsString("JDCIBeanDependentException"));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/StutterEchoTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/StutterEchoTest.java
new file mode 100644
index 0000000..bc0dc27
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/StutterEchoTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for qualified injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class StutterEchoTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][]{
+            {"alpha", "alphaalpha"},
+            {"gogol", "gogolgogol"},
+            {"elcaro", "elcaroelcaro"}
+        });
+    };
+
+    final String in, out;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param in query parameter.
+     * @param out expected output.
+     */
+    public StutterEchoTest(String in, String out) {
+        this.in = in;
+        this.out = out;
+    }
+
+    @Test
+    public void testGet() {
+        String s = target().path("stutter").queryParam("s", in).request().get(String.class);
+        assertThat(s, equalTo(out));
+    }
+}
diff --git a/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/TimerTest.java b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/TimerTest.java
new file mode 100644
index 0000000..ba4ba33
--- /dev/null
+++ b/tests/integration/cdi-test-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/TimerTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 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.cdi.resources;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.is;
+
+/**
+ * Reproducer for JERSEY-1747.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class TimerTest extends CdiTest {
+
+    @Test
+    public void testGet() {
+
+        final WebTarget target = target().path("jcdibean/dependent/timer");
+
+        final Response firstResponse = target.request().get();
+        assertThat(firstResponse.getStatus(), is(200));
+        long firstMillis = Long.decode(firstResponse.readEntity(String.class));
+        sleep(2);
+
+        final Response secondResponse = target.request().get();
+        assertThat(secondResponse.getStatus(), is(200));
+        long secondMillis = Long.decode(secondResponse.readEntity(String.class));
+
+        assertTrue("Second request should have greater millis!", secondMillis > firstMillis);
+    }
+
+    private void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ex) {
+            Logger.getLogger(TimerTest.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml
new file mode 100644
index 0000000..a491ec4
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-with-jersey-injection-custom-cfg-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-with-jersey-injection-custom-cfg-webapp</name>
+
+    <description>
+        Jersey CDI test web application, this one uses Jersey (non JAX-RS) component injection and Jersey/CDI SPI to config
+        HK2 custom bindings.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x-transaction</artifactId>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.transaction</groupId>
+            <artifactId>javax.transaction-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java
new file mode 100644
index 0000000..16f3efb
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+
+
+/**
+ * Application specific echo implementation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@AppSpecific
+@ApplicationScoped
+public class AppEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return "App: " + s;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java
new file mode 100644
index 0000000..f153c02
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, application scoped, JAX-RS resource to be injected
+ * via it's constructor from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("app-ctor-injected")
+public class AppScopedCtorInjectedResource {
+
+    // CDI injected
+    EchoService echoService;
+
+    // Jersey injected
+    Provider<ContainerRequest> request;
+    ExceptionMappers mappers;
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injected
+    MyApplication.MyInjection customInjected;
+    // Jersey/HK2 custom injected
+    @Inject
+    Hk2InjectedType hk2Injected;
+
+    // to make weld happy
+    public AppScopedCtorInjectedResource() {
+    }
+
+    @Inject
+    public AppScopedCtorInjectedResource(@AppSpecific final EchoService echoService,
+                                         final Provider<ContainerRequest> request,
+                                         final ExceptionMappers mappers,
+                                         final Provider<MonitoringStatistics> stats,
+                                         final MyApplication.MyInjection customInjected,
+                                         final Hk2InjectedType hk2Injected) {
+        this.echoService = echoService;
+        this.request = request;
+        this.mappers = mappers;
+        this.stats = stats;
+        this.customInjected = customInjected;
+        this.hk2Injected = hk2Injected;
+    }
+
+    @GET
+    public String echo(@QueryParam("s") final String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.get().getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java
new file mode 100644
index 0000000..8999f6a
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+import org.glassfish.jersey.tests.cdi.resources.MyApplication.MyInjection;
+
+/**
+ * CDI backed, application scoped, JAX-RS resource.
+ * It's fields are injected from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("app-field-injected")
+public class AppScopedFieldInjectedResource {
+
+    // CDI injected
+    @Inject
+    @AppSpecific
+    EchoService echoService;
+
+    // Jersey injected
+    @Inject
+    Provider<ContainerRequest> request;
+    @Inject
+    ExceptionMappers mappers;
+    @Inject
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injection
+    @Inject
+    MyInjection customInjected;
+    // Jersey/HK2 custom injection
+    @Inject
+    Hk2InjectedType hk2Injected;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.get().getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java
new file mode 100644
index 0000000..000fc6d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for application specific echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface AppSpecific {
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
new file mode 100644
index 0000000..8745b22
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.inject.Qualifier;
+
+/**
+ * Simple echo service to test injections using {@link Qualifier}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface EchoService {
+
+    /**
+     * Provide an echoed response.
+     *
+     * @param s String to be echoed.
+     * @return echoed input.
+     */
+    public String echo(String s);
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Hk2InjectedType.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Hk2InjectedType.java
new file mode 100644
index 0000000..d7d73ed
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/Hk2InjectedType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * CDI compliant bean. Injection will be delegated to HK2 anyway.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Hk2InjectedType {
+
+    private final String name;
+
+    /**
+     * No-arg constructor makes this bean suitable for CDI injection.
+     */
+    public Hk2InjectedType() {
+        name = "CDI would love this";
+    }
+
+    /**
+     * Hk2 custom binder is going to use this one.
+     *
+     * @param name
+     */
+    public Hk2InjectedType(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Simple getter to prove where this bean was initialized.
+     *
+     * @return name as defined at bean initialization.
+     */
+    public String getName() {
+        return name;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..a5fc240
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.internal.monitoring.MonitoringFeature;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends ResourceConfig {
+
+    public static class MyInjection {
+
+        private final String name;
+
+        public MyInjection(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    public MyApplication() {
+
+        // JAX-RS resource classes
+        register(AppScopedFieldInjectedResource.class);
+        register(AppScopedCtorInjectedResource.class);
+        register(RequestScopedFieldInjectedResource.class);
+        register(RequestScopedCtorInjectedResource.class);
+
+        register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(new MyInjection("1st: no way CDI would chime in")).to(MyInjection.class);
+                bind(new Hk2InjectedType("2nd: no way CDI would chime in")).to(Hk2InjectedType.class);
+            }
+        });
+
+        // Jersey monitoring
+        register(MonitoringFeature.class);
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyHk2TypesProvider.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyHk2TypesProvider.java
new file mode 100644
index 0000000..365e41b
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyHk2TypesProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider;
+
+/**
+ * Tell Jersey CDI extension what types should be bridged from HK2 to CDI.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class MyHk2TypesProvider implements Hk2CustomBoundTypesProvider {
+
+    @Override
+    public Set<Type> getHk2Types() {
+        return new HashSet<Type>() {{
+            add(Hk2InjectedType.class);
+            add(MyApplication.MyInjection.class);
+        }};
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java
new file mode 100644
index 0000000..e0c26bc
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * Request specific echo implementation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestSpecific
+public class RequestEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return "Request: " + s;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java
new file mode 100644
index 0000000..881895d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, request scoped, JAX-RS resource to be injected
+ * via it's constructor from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-ctor-injected")
+public class RequestScopedCtorInjectedResource {
+
+    // CDI injected
+    EchoService echoService;
+
+    // Jersey injected
+    ContainerRequest request;
+    ExceptionMappers mappers;
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injected
+    MyApplication.MyInjection customInjected;
+    // Jersey/HK2 custom injected
+    Hk2InjectedType hk2Injected;
+
+    // to make weld happy
+    public RequestScopedCtorInjectedResource() {
+    }
+
+    @Inject
+    public RequestScopedCtorInjectedResource(@RequestSpecific final EchoService echoService,
+                                             final ContainerRequest request,
+                                             final ExceptionMappers mappers,
+                                             final Provider<MonitoringStatistics> stats,
+                                             final MyApplication.MyInjection customInjected,
+                                             final Hk2InjectedType hk2Injected) {
+
+        this.echoService = echoService;
+        this.mappers = mappers;
+        this.request = request;
+        this.stats = stats;
+        this.customInjected = customInjected;
+        this.hk2Injected = hk2Injected;
+    }
+
+    @GET
+    public String echo(@QueryParam("s") final String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java
new file mode 100644
index 0000000..a24f88f
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, request scoped, JAX-RS resource.
+ * It's fields are injected from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-field-injected")
+public class RequestScopedFieldInjectedResource {
+
+    // built-in CDI bean
+    @Inject
+    BeanManager beanManager;
+
+    // CDI injected
+    @Inject
+    @RequestSpecific
+    EchoService echoService;
+
+    // Jersey injected
+    @Inject
+    ContainerRequest request;
+    @Inject
+    ExceptionMappers mappers;
+    @Inject
+    Provider<MonitoringStatistics> stats;
+
+    // Custom Jersey/HK2 injected
+    @Inject
+    MyApplication.MyInjection customInjected;
+    // Custom Jersey/HK2 injected
+    @Inject
+    Hk2InjectedType hk2Injected;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+
+    @GET
+    @Path("bm")
+    public String getBm() {
+        return beanManager.toString();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java
new file mode 100644
index 0000000..05f048d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for request specific echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface RequestSpecific {
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider
new file mode 100644
index 0000000..8ad35f8
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.cdi.resources.MyHk2TypesProvider
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/webapp/WEB-INF/web.xml b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ff67ca5
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+    <env-entry>
+        <env-entry-name>injectedResource</env-entry-name>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+        <env-entry-value>10</env-entry-value>
+    </env-entry>
+</web-app>
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
new file mode 100644
index 0000000..bafe964
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-with-jersey-injection-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-with-jersey-injection-custom-cfg-webapp").build();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java
new file mode 100644
index 0000000..0b58f6d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test custom HK2 injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class CustomInjectionTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"},
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource query parameter.
+     */
+    public CustomInjectionTest(final String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side,
+     * and the custom bound instance of {@link Hk2InjectedType} gets injected.
+     */
+    @Test
+    public void testCustomHk2Injection1() {
+        final WebTarget target = target().path(resource).path("custom");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("1st: no way CDI would chime in"));
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side,
+     * and the custom bound instance of {@link MyApplication.MyInjection} gets injected.
+     */
+    @Test
+    public void testCustomHk2Injection2() {
+        final WebTarget target = target().path(resource).path("custom2");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("2nd: no way CDI would chime in"));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java
new file mode 100644
index 0000000..5675349
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for exception mapper injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ExceptionMappersTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"},
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource query parameter.
+     */
+    public ExceptionMappersTest(final String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side, and for two
+     * the injected mappers remains the same across requests.
+     */
+    @Test
+    public void testMappersNotNull() {
+        final WebTarget target = target().path(resource).path("mappers");
+        final Response firstResponse = target.request().get();
+        assertThat(firstResponse.getStatus(), equalTo(200));
+        final String firstValue = firstResponse.readEntity(String.class);
+        assertThat(target.request().get(String.class), equalTo(firstValue));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java
new file mode 100644
index 0000000..25aed86
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for monitoring statistics injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class MonitoringTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"}
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource uri of resource to be tested.
+     */
+    public MonitoringTest(final String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Make several requests and check the counter keeps incrementing.
+     *
+     * @throws Exception in case of unexpected test failure.
+     */
+    @Test
+    public void testRequestCount() throws Exception {
+        final WebTarget target = target().path(resource).path("requestCount");
+        Thread.sleep(1000); // this is to allow statistics on the server side to get updated
+        final int start = Integer.decode(target.request().get(String.class));
+        for (int i = 1; i < 4; i++) {
+            Thread.sleep(1000); // this is to allow statistics on the server side to get updated
+            final int next = Integer.decode(target.request().get(String.class));
+            assertThat(String.format("testing %s", resource), next, equalTo(start + i));
+        }
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java
new file mode 100644
index 0000000..a31befa
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-cfg-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test injection of request depending instances works as expected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class RequestSensitiveTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected", "alpha", "App: alpha"},
+                {"app-field-injected", "gogol", "App: gogol"},
+                {"app-field-injected", "elcaro", "App: elcaro"},
+                {"app-ctor-injected", "alpha", "App: alpha"},
+                {"app-ctor-injected", "gogol", "App: gogol"},
+                {"app-ctor-injected", "elcaro", "App: elcaro"},
+                {"request-field-injected", "alpha", "Request: alpha"},
+                {"request-field-injected", "gogol", "Request: gogol"},
+                {"request-field-injected", "oracle", "Request: oracle"},
+                {"request-ctor-injected", "alpha", "Request: alpha"},
+                {"request-ctor-injected", "gogol", "Request: gogol"},
+                {"request-ctor-injected", "oracle", "Request: oracle"}
+        });
+    }
+
+    final String resource, straight, echoed;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource uri of the resource to be tested.
+     * @param straight request specific input.
+     * @param echoed   CDI injected service should produce this out of previous, straight, parameter.
+     */
+    public RequestSensitiveTest(final String resource, final String straight, final String echoed) {
+        this.resource = resource;
+        this.straight = straight;
+        this.echoed = echoed;
+    }
+
+    @Test
+    public void testCdiInjection() {
+        final String s = target().path(resource).queryParam("s", straight).request().get(String.class);
+        assertThat(s, equalTo(echoed));
+    }
+
+    @Test
+    public void testHk2Injection() {
+        final String s = target().path(resource).path("path").path(straight).request().get(String.class);
+        assertThat(s, equalTo(String.format("%s/path/%s", resource, straight)));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml
new file mode 100644
index 0000000..5980238
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-with-jersey-injection-custom-hk2-banned-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-with-jersey-injection-custom-hk2-banned-webapp</name>
+
+    <description>
+        Jersey CDI test web application, this one uses Jersey (non JAX-RS) component injection HK2 custom binding has been banned.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x-transaction</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.transaction</groupId>
+            <artifactId>javax.transaction-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x-ban-custom-hk2-binding</artifactId>
+            <scope>compile</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.glassfish.jersey.core</groupId>
+                    <artifactId>jersey-common</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>javax.ws.rs</groupId>
+                    <artifactId>javax.ws.rs-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+         <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java
new file mode 100644
index 0000000..16f3efb
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+
+
+/**
+ * Application specific echo implementation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@AppSpecific
+@ApplicationScoped
+public class AppEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return "App: " + s;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java
new file mode 100644
index 0000000..a50a688
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, application scoped, JAX-RS resource to be injected
+ * via it's constructor from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("app-ctor-injected")
+public class AppScopedCtorInjectedResource {
+
+    // CDI injected
+    EchoService echoService;
+
+    // Jersey injected
+    Provider<ContainerRequest> request;
+    ExceptionMappers mappers;
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injected
+    MyApplication.MyInjection customInjected;
+    // Jersey/HK2 custom injected
+    @Inject
+    CdiInjectedType hk2Injected;
+
+    // to make weld happy
+    public AppScopedCtorInjectedResource() {
+    }
+
+    @Inject
+    public AppScopedCtorInjectedResource(@AppSpecific final EchoService echoService,
+                                         final Provider<ContainerRequest> request,
+                                         final ExceptionMappers mappers,
+                                         final Provider<MonitoringStatistics> stats,
+                                         final MyApplication.MyInjection customInjected,
+                                         final CdiInjectedType hk2Injected) {
+        this.echoService = echoService;
+        this.request = request;
+        this.mappers = mappers;
+        this.stats = stats;
+        this.customInjected = customInjected;
+        this.hk2Injected = hk2Injected;
+    }
+
+    @GET
+    public String echo(@QueryParam("s") final String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.get().getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java
new file mode 100644
index 0000000..8c16474
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+import org.glassfish.jersey.tests.cdi.resources.MyApplication.MyInjection;
+
+/**
+ * CDI backed, application scoped, JAX-RS resource.
+ * It's fields are injected from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("app-field-injected")
+public class AppScopedFieldInjectedResource {
+
+    // CDI injected
+    @Inject
+    @AppSpecific
+    EchoService echoService;
+
+    // Jersey injected
+    @Inject
+    Provider<ContainerRequest> request;
+    @Inject
+    ExceptionMappers mappers;
+    @Inject
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injection
+    @Inject
+    MyInjection customInjected;
+    // Jersey/HK2 custom injection
+    @Inject
+    CdiInjectedType hk2Injected;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.get().getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java
new file mode 100644
index 0000000..000fc6d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for application specific echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface AppSpecific {
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiInjectedType.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiInjectedType.java
new file mode 100644
index 0000000..84b1fc2
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/CdiInjectedType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * CDI compliant bean. Injection will be done by CDI in this application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiInjectedType {
+
+    private final String name;
+
+    /**
+     * No-arg constructor makes this bean suitable for CDI injection.
+     */
+    public CdiInjectedType() {
+        name = "CDI would love this";
+    }
+
+    /**
+     * Hk2 custom binder is going to use this one.
+     *
+     * @param name
+     */
+    public CdiInjectedType(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Simple getter to prove where this bean was initialized.
+     *
+     * @return name as defined at bean initialization.
+     */
+    public String getName() {
+        return name;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
new file mode 100644
index 0000000..8745b22
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.inject.Qualifier;
+
+/**
+ * Simple echo service to test injections using {@link Qualifier}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface EchoService {
+
+    /**
+     * Provide an echoed response.
+     *
+     * @param s String to be echoed.
+     * @return echoed input.
+     */
+    public String echo(String s);
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..829f43a
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.internal.monitoring.MonitoringFeature;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends ResourceConfig {
+
+    public static class MyInjection {
+
+        private final String name;
+
+        public MyInjection() {
+            name = "CDI injected";
+        }
+
+        public MyInjection(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    public MyApplication() {
+
+        // JAX-RS resource classes
+        register(AppScopedFieldInjectedResource.class);
+        register(AppScopedCtorInjectedResource.class);
+        register(RequestScopedFieldInjectedResource.class);
+        register(RequestScopedCtorInjectedResource.class);
+
+        register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(new MyInjection("1st: unused HK2 binding")).to(MyInjection.class);
+                bind(new CdiInjectedType("2nd: unused HK2 binding")).to(CdiInjectedType.class);
+            }
+        });
+
+        // Jersey monitoring
+        register(MonitoringFeature.class);
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java
new file mode 100644
index 0000000..e0c26bc
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * Request specific echo implementation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestSpecific
+public class RequestEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return "Request: " + s;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java
new file mode 100644
index 0000000..0080e0d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, request scoped, JAX-RS resource to be injected
+ * via it's constructor from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-ctor-injected")
+public class RequestScopedCtorInjectedResource {
+
+    // CDI injected
+    EchoService echoService;
+
+    // Jersey injected
+    ContainerRequest request;
+    ExceptionMappers mappers;
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injected
+    MyApplication.MyInjection customInjected;
+    // Jersey/HK2 custom injected
+    CdiInjectedType hk2Injected;
+
+    // to make weld happy
+    public RequestScopedCtorInjectedResource() {
+    }
+
+    @Inject
+    public RequestScopedCtorInjectedResource(@RequestSpecific final EchoService echoService,
+                                             final ContainerRequest request,
+                                             final ExceptionMappers mappers,
+                                             final Provider<MonitoringStatistics> stats,
+                                             final MyApplication.MyInjection customInjected,
+                                             final CdiInjectedType hk2Injected) {
+
+        this.echoService = echoService;
+        this.mappers = mappers;
+        this.request = request;
+        this.stats = stats;
+        this.customInjected = customInjected;
+        this.hk2Injected = hk2Injected;
+    }
+
+    @GET
+    public String echo(@QueryParam("s") final String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java
new file mode 100644
index 0000000..eecf01e
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, request scoped, JAX-RS resource.
+ * It's fields are injected from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-field-injected")
+public class RequestScopedFieldInjectedResource {
+
+    // built-in CDI bean
+    @Inject
+    BeanManager beanManager;
+
+    // CDI injected
+    @Inject
+    @RequestSpecific
+    EchoService echoService;
+
+    // Jersey injected
+    @Inject
+    ContainerRequest request;
+    @Inject
+    ExceptionMappers mappers;
+    @Inject
+    Provider<MonitoringStatistics> stats;
+
+    // Custom Jersey/HK2 injected
+    @Inject
+    MyApplication.MyInjection customInjected;
+    // Custom Jersey/HK2 injected
+    @Inject
+    CdiInjectedType hk2Injected;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("custom2")
+    public String getCustom2() {
+        return hk2Injected.getName();
+    }
+
+    @GET
+    @Path("bm")
+    public String getBm() {
+        return beanManager.toString();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java
new file mode 100644
index 0000000..05f048d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for request specific echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface RequestSpecific {
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/webapp/WEB-INF/web.xml b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ff67ca5
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+    <env-entry>
+        <env-entry-name>injectedResource</env-entry-name>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+        <env-entry-value>10</env-entry-value>
+    </env-entry>
+</web-app>
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
new file mode 100644
index 0000000..89cf238
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-with-jersey-injection-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-with-jersey-injection-custom-hk2-banned-webapp").build();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java
new file mode 100644
index 0000000..6498726
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test custom HK2 injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class CustomInjectionTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"},
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource query parameter.
+     */
+    public CustomInjectionTest(final String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side,
+     * and the custom bound instance of {@link CdiInjectedType} gets CDI injected.
+     */
+    @Test
+    public void testCustomHk2Injection1() {
+        final WebTarget target = target().path(resource).path("custom");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("CDI injected"));
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side,
+     * and the custom bound instance of {@link MyApplication.MyInjection} gets CDI injected.
+     */
+    @Test
+    public void testCustomHk2Injection2() {
+        final WebTarget target = target().path(resource).path("custom2");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("CDI would love this"));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java
new file mode 100644
index 0000000..e87369f
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for exception mapper injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ExceptionMappersTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"}
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource query parameter.
+     */
+    public ExceptionMappersTest(final String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side, and for two
+     * the injected mappers remains the same across requests.
+     */
+    @Test
+    public void testMappersNotNull() {
+        final WebTarget target = target().path(resource).path("mappers");
+        final Response firstResponse = target.request().get();
+        assertThat(firstResponse.getStatus(), equalTo(200));
+        final String firstValue = firstResponse.readEntity(String.class);
+        assertThat(target.request().get(String.class), equalTo(firstValue));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java
new file mode 100644
index 0000000..25aed86
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for monitoring statistics injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class MonitoringTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"}
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource uri of resource to be tested.
+     */
+    public MonitoringTest(final String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Make several requests and check the counter keeps incrementing.
+     *
+     * @throws Exception in case of unexpected test failure.
+     */
+    @Test
+    public void testRequestCount() throws Exception {
+        final WebTarget target = target().path(resource).path("requestCount");
+        Thread.sleep(1000); // this is to allow statistics on the server side to get updated
+        final int start = Integer.decode(target.request().get(String.class));
+        for (int i = 1; i < 4; i++) {
+            Thread.sleep(1000); // this is to allow statistics on the server side to get updated
+            final int next = Integer.decode(target.request().get(String.class));
+            assertThat(String.format("testing %s", resource), next, equalTo(start + i));
+        }
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java
new file mode 100644
index 0000000..a31befa
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-custom-hk2-banned-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test injection of request depending instances works as expected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class RequestSensitiveTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected", "alpha", "App: alpha"},
+                {"app-field-injected", "gogol", "App: gogol"},
+                {"app-field-injected", "elcaro", "App: elcaro"},
+                {"app-ctor-injected", "alpha", "App: alpha"},
+                {"app-ctor-injected", "gogol", "App: gogol"},
+                {"app-ctor-injected", "elcaro", "App: elcaro"},
+                {"request-field-injected", "alpha", "Request: alpha"},
+                {"request-field-injected", "gogol", "Request: gogol"},
+                {"request-field-injected", "oracle", "Request: oracle"},
+                {"request-ctor-injected", "alpha", "Request: alpha"},
+                {"request-ctor-injected", "gogol", "Request: gogol"},
+                {"request-ctor-injected", "oracle", "Request: oracle"}
+        });
+    }
+
+    final String resource, straight, echoed;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource uri of the resource to be tested.
+     * @param straight request specific input.
+     * @param echoed   CDI injected service should produce this out of previous, straight, parameter.
+     */
+    public RequestSensitiveTest(final String resource, final String straight, final String echoed) {
+        this.resource = resource;
+        this.straight = straight;
+        this.echoed = echoed;
+    }
+
+    @Test
+    public void testCdiInjection() {
+        final String s = target().path(resource).queryParam("s", straight).request().get(String.class);
+        assertThat(s, equalTo(echoed));
+    }
+
+    @Test
+    public void testHk2Injection() {
+        final String s = target().path(resource).path("path").path(straight).request().get(String.class);
+        assertThat(s, equalTo(String.format("%s/path/%s", resource, straight)));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/pom.xml b/tests/integration/cdi-with-jersey-injection-webapp/pom.xml
new file mode 100644
index 0000000..d0e0c42
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>cdi-with-jersey-injection-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-cdi-with-jersey-injection-webapp</name>
+
+    <description>Jersey CDI test web application, this one uses Jersey (non JAX-RS) component injection</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x-transaction</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java
new file mode 100644
index 0000000..16f3efb
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppEcho.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.enterprise.context.ApplicationScoped;
+
+
+/**
+ * Application specific echo implementation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@AppSpecific
+@ApplicationScoped
+public class AppEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return "App: " + s;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java
new file mode 100644
index 0000000..2bffeea
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedCtorInjectedResource.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, application scoped, JAX-RS resource to be injected
+ * via it's constructor from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("app-ctor-injected")
+public class AppScopedCtorInjectedResource {
+
+    // CDI injected
+    EchoService echoService;
+
+    // Jersey injected
+    Provider<ContainerRequest> request;
+    ExceptionMappers mappers;
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injected
+    MyApplication.MyInjection customInjected;
+
+    // to make weld happy
+    public AppScopedCtorInjectedResource() {
+    }
+
+    @Inject
+    public AppScopedCtorInjectedResource(@AppSpecific final EchoService echoService,
+                                         final Provider<ContainerRequest> request,
+                                         final ExceptionMappers mappers,
+                                         final Provider<MonitoringStatistics> stats,
+                                         final MyApplication.MyInjection customInjected) {
+        this.echoService = echoService;
+        this.request = request;
+        this.mappers = mappers;
+        this.stats = stats;
+        this.customInjected = customInjected;
+    }
+
+    @GET
+    public String echo(@QueryParam("s") final String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.get().getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java
new file mode 100644
index 0000000..4c196d3
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppScopedFieldInjectedResource.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+import org.glassfish.jersey.tests.cdi.resources.MyApplication.MyInjection;
+
+/**
+ * CDI backed, application scoped, JAX-RS resource.
+ * It's fields are injected from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationScoped
+@Path("app-field-injected")
+public class AppScopedFieldInjectedResource {
+
+    // CDI injected
+    @Inject
+    @AppSpecific
+    EchoService echoService;
+
+    // Jersey injected
+    @Inject
+    Provider<ContainerRequest> request;
+    @Inject
+    ExceptionMappers mappers;
+    @Inject
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injection
+    @Inject
+    MyInjection customInjected;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.get().getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java
new file mode 100644
index 0000000..000fc6d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/AppSpecific.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for application specific echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface AppSpecific {
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
new file mode 100644
index 0000000..8745b22
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/EchoService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.inject.Qualifier;
+
+/**
+ * Simple echo service to test injections using {@link Qualifier}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface EchoService {
+
+    /**
+     * Provide an echoed response.
+     *
+     * @param s String to be echoed.
+     * @return echoed input.
+     */
+    public String echo(String s);
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
new file mode 100644
index 0000000..027c907
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.internal.monitoring.MonitoringFeature;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends ResourceConfig {
+
+    public static class MyInjection {
+
+        private final String name;
+
+        public MyInjection(final String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    public MyApplication() {
+        // JAX-RS resource classes
+        register(AppScopedFieldInjectedResource.class);
+        register(AppScopedCtorInjectedResource.class);
+        register(RequestScopedFieldInjectedResource.class);
+        register(RequestScopedCtorInjectedResource.class);
+
+        register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(new MyInjection("no way CDI would chime in")).to(MyInjection.class);
+            }
+        });
+
+        // Jersey monitoring
+        register(MonitoringFeature.class);
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyHk2TypesProvider.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyHk2TypesProvider.java
new file mode 100644
index 0000000..362adf3
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyHk2TypesProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider;
+
+/**
+ * Tell Jersey CDI extension what types should be bridged from HK2 to CDI.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class MyHk2TypesProvider implements Hk2CustomBoundTypesProvider {
+
+    @Override
+    public Set<Type> getHk2Types() {
+        return new HashSet<Type>() {{
+            add(MyApplication.MyInjection.class);
+        }};
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java
new file mode 100644
index 0000000..e0c26bc
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestEcho.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+/**
+ * Request specific echo implementation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestSpecific
+public class RequestEcho implements EchoService {
+
+    @Override
+    public String echo(String s) {
+        return "Request: " + s;
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java
new file mode 100644
index 0000000..250c0d8
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedCtorInjectedResource.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, request scoped, JAX-RS resource to be injected
+ * via it's constructor from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-ctor-injected")
+public class RequestScopedCtorInjectedResource {
+
+    // CDI injected
+    EchoService echoService;
+
+    // Jersey injected
+    ContainerRequest request;
+    ExceptionMappers mappers;
+    Provider<MonitoringStatistics> stats;
+
+    // Jersey/HK2 custom injected
+    MyApplication.MyInjection customInjected;
+
+    // to make weld happy
+    public RequestScopedCtorInjectedResource() {
+    }
+
+    @Inject
+    public RequestScopedCtorInjectedResource(@RequestSpecific EchoService echoService,
+                                             ContainerRequest request, ExceptionMappers mappers,
+                                             Provider<MonitoringStatistics> stats, MyApplication.MyInjection customInjected) {
+
+        this.echoService = echoService;
+        this.mappers = mappers;
+        this.request = request;
+        this.stats = stats;
+        this.customInjected = customInjected;
+    }
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java
new file mode 100644
index 0000000..e762601
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestScopedFieldInjectedResource.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.monitoring.MonitoringStatistics;
+import org.glassfish.jersey.spi.ExceptionMappers;
+
+/**
+ * CDI backed, request scoped, JAX-RS resource.
+ * It's fields are injected from both CDI and Jersey HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("request-field-injected")
+public class RequestScopedFieldInjectedResource {
+
+    // built-in CDI bean
+    @Inject
+    BeanManager beanManager;
+
+    // CDI injected
+    @Inject
+    @RequestSpecific
+    EchoService echoService;
+
+    // Jersey injected
+    @Inject
+    ContainerRequest request;
+    @Inject
+    ExceptionMappers mappers;
+    @Inject
+    Provider<MonitoringStatistics> stats;
+
+    // Custom Jersey/HK2 injected
+    @Inject
+    MyApplication.MyInjection customInjected;
+
+    @GET
+    public String echo(@QueryParam("s") String s) {
+        return echoService.echo(s);
+    }
+
+    @GET
+    @Path("path/{param}")
+    public String getPath() {
+        return request.getPath(true);
+    }
+
+    @GET
+    @Path("mappers")
+    public String getMappers() {
+        return mappers.toString();
+    }
+
+    @GET
+    @Path("requestCount")
+    public String getStatisticsProperty() {
+        return String.valueOf(stats.get().snapshot().getRequestStatistics().getTimeWindowStatistics().get(0L).getRequestCount());
+    }
+
+    @GET
+    @Path("custom")
+    public String getCustom() {
+        return customInjected.getName();
+    }
+
+    @GET
+    @Path("bm")
+    public String getBm() {
+        return beanManager.toString();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java
new file mode 100644
index 0000000..05f048d
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/RequestSpecific.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for request specific echo service.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface RequestSpecific {
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider b/tests/integration/cdi-with-jersey-injection-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider
new file mode 100644
index 0000000..8ad35f8
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/resources/META-INF/services/org.glassfish.jersey.ext.cdi1x.spi.Hk2CustomBoundTypesProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.cdi.resources.MyHk2TypesProvider
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/webapp/WEB-INF/beans.xml b/tests/integration/cdi-with-jersey-injection-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/main/webapp/WEB-INF/web.xml b/tests/integration/cdi-with-jersey-injection-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ff67ca5
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+    <env-entry>
+        <env-entry-name>injectedResource</env-entry-name>
+        <env-entry-type>java.lang.Integer</env-entry-type>
+        <env-entry-value>10</env-entry-value>
+    </env-entry>
+</web-app>
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
new file mode 100644
index 0000000..1273dd1
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CdiTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+/**
+ * Test for CDI web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/cdi-with-jersey-injection-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CdiTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("cdi-with-jersey-injection-webapp").build();
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java
new file mode 100644
index 0000000..4878426
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/CustomInjectionTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test custom HK2 injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class CustomInjectionTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"}
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource query parameter.
+     */
+    public CustomInjectionTest(String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side,
+     * and the custom bound instance gets injected.
+     */
+    @Test
+    public void testCustomHk2InjectionNull() {
+        WebTarget target = target().path(resource).path("custom");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("no way CDI would chime in"));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java
new file mode 100644
index 0000000..4020db3
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/ExceptionMappersTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for exception mapper injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class ExceptionMappersTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"}
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource query parameter.
+     */
+    public ExceptionMappersTest(String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Check that for one no NPE happens on the server side, and for two
+     * the injected mappers remains the same across requests.
+     */
+    @Test
+    public void testMappersNotNull() {
+        WebTarget target = target().path(resource).path("mappers");
+        final Response firstResponse = target.request().get();
+        assertThat(firstResponse.getStatus(), equalTo(200));
+        String firstValue = firstResponse.readEntity(String.class);
+        assertThat(target.request().get(String.class), equalTo(firstValue));
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java
new file mode 100644
index 0000000..f364be2
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/MonitoringTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for monitoring statistics injection.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class MonitoringTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected"},
+                {"app-ctor-injected"},
+                {"request-field-injected"},
+                {"request-ctor-injected"}
+        });
+    }
+
+    final String resource;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource uri of resource to be tested.
+     */
+    public MonitoringTest(String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Make several requests and check the counter keeps incrementing.
+     *
+     * @throws Exception in case of unexpected test failure.
+     */
+    @Test
+    public void testRequestCount() throws Exception {
+        WebTarget target = target().path(resource).path("requestCount");
+        Thread.sleep(1000); // this is to allow statistics on the server side to get updated
+        int start = Integer.decode(target.request().get(String.class));
+        for (int i = 1; i < 4; i++) {
+            Thread.sleep(1000); // this is to allow statistics on the server side to get updated
+            int next = Integer.decode(target.request().get(String.class));
+            assertThat(String.format("testing %s", resource), next, equalTo(start + i));
+        }
+    }
+}
diff --git a/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java
new file mode 100644
index 0000000..ff41909
--- /dev/null
+++ b/tests/integration/cdi-with-jersey-injection-webapp/src/test/java/org/glassfish/jersey/tests/cdi/resources/RequestSensitiveTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, 2018 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.cdi.resources;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test injection of request depending instances works as expected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class RequestSensitiveTest extends CdiTest {
+
+    @Parameterized.Parameters
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"app-field-injected", "alpha", "App: alpha"},
+                {"app-field-injected", "gogol", "App: gogol"},
+                {"app-field-injected", "elcaro", "App: elcaro"},
+                {"app-ctor-injected", "alpha", "App: alpha"},
+                {"app-ctor-injected", "gogol", "App: gogol"},
+                {"app-ctor-injected", "elcaro", "App: elcaro"},
+                {"request-field-injected", "alpha", "Request: alpha"},
+                {"request-field-injected", "gogol", "Request: gogol"},
+                {"request-field-injected", "oracle", "Request: oracle"},
+                {"request-ctor-injected", "alpha", "Request: alpha"},
+                {"request-ctor-injected", "gogol", "Request: gogol"},
+                {"request-ctor-injected", "oracle", "Request: oracle"}
+        });
+    }
+
+    final String resource, straight, echoed;
+
+    /**
+     * Construct instance with the above test data injected.
+     *
+     * @param resource uri of the resource to be tested.
+     * @param straight request specific input.
+     * @param echoed CDI injected service should produce this out of previous, straight, parameter.
+     */
+    public RequestSensitiveTest(String resource, String straight, String echoed) {
+        this.resource = resource;
+        this.straight = straight;
+        this.echoed = echoed;
+    }
+
+    @Test
+    public void testCdiInjection() {
+        String s = target().path(resource).queryParam("s", straight).request().get(String.class);
+        assertThat(s, equalTo(echoed));
+    }
+
+    @Test
+    public void testHk2Injection() {
+        String s = target().path(resource).path("path").path(straight).request().get(String.class);
+        assertThat(s, equalTo(String.format("%s/path/%s", resource, straight)));
+    }
+}
diff --git a/tests/integration/client-connector-provider/pom.xml b/tests/integration/client-connector-provider/pom.xml
new file mode 100644
index 0000000..465f8e6
--- /dev/null
+++ b/tests/integration/client-connector-provider/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>client-connector-provider</artifactId>
+    <packaging>jar</packaging>
+    <name>client-connector-provider</name>
+
+    <description>Client Connector provider test</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+</project>
diff --git a/tests/integration/client-connector-provider/src/main/java/org/glassfish/jersey/tests/integration/client/connector/provider/CustomConnectorProvider.java b/tests/integration/client-connector-provider/src/main/java/org/glassfish/jersey/tests/integration/client/connector/provider/CustomConnectorProvider.java
new file mode 100644
index 0000000..5a4e70b
--- /dev/null
+++ b/tests/integration/client-connector-provider/src/main/java/org/glassfish/jersey/tests/integration/client/connector/provider/CustomConnectorProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.client.connector.provider;
+
+import java.net.HttpURLConnection;
+
+import javax.ws.rs.client.Client;
+
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.internal.HttpUrlConnector;
+import org.glassfish.jersey.client.spi.Connector;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public final class CustomConnectorProvider extends HttpUrlConnectorProvider {
+
+    public static volatile boolean invoked = false;
+
+    @Override
+    protected Connector createHttpUrlConnector(Client client, ConnectionFactory connectionFactory, int chunkSize,
+                                               boolean fixLengthStreaming, boolean setMethodWorkaround) {
+
+        return new HttpUrlConnector(
+                client,
+                connectionFactory,
+                chunkSize,
+                fixLengthStreaming,
+                setMethodWorkaround) {
+
+            @Override
+            protected void secureConnection(JerseyClient client, HttpURLConnection uc) {
+                invoked = true;
+            }
+        };
+    }
+}
diff --git a/tests/integration/client-connector-provider/src/main/java/org/glassfish/jersey/tests/integration/client/connector/provider/TestResource.java b/tests/integration/client-connector-provider/src/main/java/org/glassfish/jersey/tests/integration/client/connector/provider/TestResource.java
new file mode 100644
index 0000000..f941b89
--- /dev/null
+++ b/tests/integration/client-connector-provider/src/main/java/org/glassfish/jersey/tests/integration/client/connector/provider/TestResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.client.connector.provider;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("test")
+public class TestResource {
+
+    @GET
+    public String get() {
+        return "test";
+    }
+}
diff --git a/tests/integration/client-connector-provider/src/main/resources/META-INF/services/org.glassfish.jersey.client.spi.ConnectorProvider b/tests/integration/client-connector-provider/src/main/resources/META-INF/services/org.glassfish.jersey.client.spi.ConnectorProvider
new file mode 100644
index 0000000..b3bb97e
--- /dev/null
+++ b/tests/integration/client-connector-provider/src/main/resources/META-INF/services/org.glassfish.jersey.client.spi.ConnectorProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.integration.client.connector.provider.CustomConnectorProvider
diff --git a/tests/integration/client-connector-provider/src/test/java/org/glassfish/jersey/tests/integration/client/connector/provider/CustomConnectorProviderTest.java b/tests/integration/client-connector-provider/src/test/java/org/glassfish/jersey/tests/integration/client/connector/provider/CustomConnectorProviderTest.java
new file mode 100644
index 0000000..dc4daf1
--- /dev/null
+++ b/tests/integration/client-connector-provider/src/test/java/org/glassfish/jersey/tests/integration/client/connector/provider/CustomConnectorProviderTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.client.connector.provider;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class CustomConnectorProviderTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(TestResource.class);
+    }
+
+    @Test
+    public void testInvoked() {
+        assertFalse(CustomConnectorProvider.invoked);
+
+        Response response = target().path("test").request("text/plain").get();
+        assertEquals(200, response.getStatus());
+
+        assertTrue(CustomConnectorProvider.invoked);
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/ear/pom.xml b/tests/integration/ejb-multimodule-reload/ear/pom.xml
new file mode 100644
index 0000000..09183cb
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/ear/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-reload-ear</artifactId>
+    <packaging>ear</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-reload-ear</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-ear-plugin</artifactId>
+                <configuration>
+                    <version>6</version>
+                    <defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
+                    <modules>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>ejb-multimodule-reload-war1</artifactId>
+                        </webModule>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>ejb-multimodule-reload-war2</artifactId>
+                        </webModule>
+                        <ejbModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>ejb-multimodule-reload-lib</artifactId>
+                        </ejbModule>
+                    </modules>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>ejb-multimodule-reload-war1</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>ejb-multimodule-reload-war2</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>ejb-multimodule-reload-lib</artifactId>
+            <type>ejb</type>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/integration/ejb-multimodule-reload/lib/pom.xml b/tests/integration/ejb-multimodule-reload/lib/pom.xml
new file mode 100644
index 0000000..730d296
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/lib/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-reload-lib</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-reload-lib</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ContainerListener.java b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ContainerListener.java
new file mode 100644
index 0000000..11de9c9
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ContainerListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.reload.lib;
+
+import javax.ejb.EJB;
+import javax.ejb.Singleton;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+
+/**
+ * JAX-RS resource that keeps number of reloads.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@Singleton
+public class ContainerListener extends AbstractContainerLifecycleListener {
+
+    @EJB EjbReloaderService reloader;
+
+    @Override
+    public void onStartup(final Container container) {
+        reloader.setContainer(container);
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/EjbReloaderService.java b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/EjbReloaderService.java
new file mode 100644
index 0000000..fc95ca8
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/EjbReloaderService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.reload.lib;
+
+import javax.ejb.Singleton;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.Container;
+
+/**
+ * Singleton EJB bean that is used to reload the first web application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+public class EjbReloaderService {
+
+    Container container;
+
+    public void reload() {
+        container.reload(newApp());
+    }
+
+    /**
+     * Create new resource config that contains {@link ReloadDetectionResource} singleton
+     * with current nano time, so that we can detect when the application has been initialized.
+     *
+     * @return new resource config.
+     */
+    private ResourceConfig newApp() {
+
+        ResourceConfig result = new ResourceConfig();
+        result.register(ReloadDetectionResource.createNewInstance());
+        result.register(ContainerListener.class);
+        return result;
+    }
+
+    /**
+     * Set the container to be reloaded. Invoked from {@link ContainerListener}.
+     *
+     * @param container to be reloaded.
+     */
+    public void setContainer(Container container) {
+        this.container = container;
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ReloadDetectionResource.java b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ReloadDetectionResource.java
new file mode 100644
index 0000000..95cbf5b
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ReloadDetectionResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.reload.lib;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import javax.inject.Singleton;
+
+/**
+ * JAX-RS resource registered as a singleton
+ * allows to detect when application got initiated as the value
+ * returned from its getNano resource method
+ * will get adjusted with each reload.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("last-init-nano-time")
+public class ReloadDetectionResource {
+
+    final long ns = System.nanoTime();
+
+    private ReloadDetectionResource() {
+        // prevent instantiation
+    }
+
+    /**
+     * This is the only mean how to get a new instance of the resource.
+     *
+     * @return new reload detection resource.
+     */
+    public static final ReloadDetectionResource createNewInstance() {
+        return new ReloadDetectionResource();
+    }
+
+
+    @GET
+    public long getNano() {
+        return ns;
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ReloaderResource.java b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ReloaderResource.java
new file mode 100644
index 0000000..ff2ce6d
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/lib/ReloaderResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.reload.lib;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * JAX-RS resource used to reload the first application.
+ * This resource is only registered inside the second application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@Path("reload")
+public class ReloaderResource {
+
+    @EJB EjbReloaderService ejbReloaderService;
+
+    @GET
+    public void reloadTheOtherApp() {
+        ejbReloaderService.reload();
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/lib/src/main/resources/META-INF/beans.xml b/tests/integration/ejb-multimodule-reload/lib/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..3b46d69
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/lib/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/ejb-multimodule-reload/pom.xml b/tests/integration/ejb-multimodule-reload/pom.xml
new file mode 100644
index 0000000..35fd46f
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>ejb-multimodule-reload</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-reload</name>
+
+    <description>
+        EJB Multi-Module Reload
+    </description>
+
+    <modules>
+        <module>ear</module>
+        <module>lib</module>
+        <module>war1</module>
+        <module>war2</module>
+    </modules>
+</project>
diff --git a/tests/integration/ejb-multimodule-reload/war1/pom.xml b/tests/integration/ejb-multimodule-reload/war1/pom.xml
new file mode 100644
index 0000000..f90e657
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war1/pom.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-reload-war1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-reload-war1</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ejb-multimodule-reload-lib</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2.external</groupId>
+            <artifactId>javax.inject</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.inject</groupId>
+                    <artifactId>javax.inject</artifactId>
+                </exclusion>
+            </exclusions>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/ejb-multimodule-reload/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web1/FirstApp.java b/tests/integration/ejb-multimodule-reload/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web1/FirstApp.java
new file mode 100644
index 0000000..f24d546
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war1/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web1/FirstApp.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.reload.web1;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.tests.integration.multimodule.ejb.reload.lib.ContainerListener;
+import org.glassfish.jersey.tests.integration.multimodule.ejb.reload.lib.ReloadDetectionResource;
+
+/**
+ * Initial JAX-RS application for the first web application.
+ * This one will get reloaded.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/")
+public class FirstApp extends Application {
+
+    static final Set<Object> SINGLETONS = new HashSet<Object>() {{
+        add(ReloadDetectionResource.createNewInstance());
+    }};
+
+    static final Set<Class<?>> CLASSES = new HashSet<Class<?>>() {{
+        add(ContainerListener.class);
+    }};
+
+    @Override
+    public Set<Object> getSingletons() {
+        return SINGLETONS;
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return CLASSES;
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/war1/src/main/resources/META-INF/beans.xml b/tests/integration/ejb-multimodule-reload/war1/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war1/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/ejb-multimodule-reload/war1/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web1/ReloadTest.java b/tests/integration/ejb-multimodule-reload/war1/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web1/ReloadTest.java
new file mode 100644
index 0000000..4d854b0
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war1/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web1/ReloadTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.reload.web1;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test reload functionality for two web app test case.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy ../ear/target/ejb-multimodule-reload-ear-*.ear
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ReloadTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new FirstApp();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testReload() {
+
+        final WebTarget nanosTarget = target().path("ejb-multimodule-reload-war1/last-init-nano-time");
+
+        final long nanos1 = _readInitTimeNanos(nanosTarget);
+        final long nanos2 = _readInitTimeNanos(nanosTarget);
+
+        assertThat(nanos2, is(equalTo(nanos1)));
+
+        // J-592 reproducer:
+
+//        reload();
+//
+//        final long nanos3 = _readInitTimeNanos(nanosTarget);
+//        final long nanos4 = _readInitTimeNanos(nanosTarget);
+//
+//        assertThat(nanos4, is(equalTo(nanos3)));
+//        assertThat(nanos3, is(greaterThan(nanos2)));
+//
+//        reload();
+//
+//        final long nanos5 = _readInitTimeNanos(nanosTarget);
+//        final long nanos6 = _readInitTimeNanos(nanosTarget);
+//
+//        assertThat(nanos6, is(equalTo(nanos5)));
+//        assertThat(nanos5, is(greaterThan(nanos4)));
+
+        // END: J-592 reproducer
+    }
+
+    private void reload() {
+        final WebTarget reloadTarget = target().path("ejb-multimodule-reload-war2/reload");
+        assertThat(reloadTarget.request().get().getStatus(), is(204));
+    }
+
+    private long _readInitTimeNanos(final WebTarget target) throws NumberFormatException {
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), is(200));
+        return Long.parseLong(response.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/war2/pom.xml b/tests/integration/ejb-multimodule-reload/war2/pom.xml
new file mode 100644
index 0000000..1a3bf25
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war2/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-reload-war2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-reload-war2</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ejb-multimodule-reload-lib</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/ejb-multimodule-reload/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web2/SecondApp.java b/tests/integration/ejb-multimodule-reload/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web2/SecondApp.java
new file mode 100644
index 0000000..89d2304
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war2/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web2/SecondApp.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.reload.web2;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.tests.integration.multimodule.ejb.reload.lib.ReloaderResource;
+
+/**
+ * JAX-RS application from which we are going to reload
+ * the other one.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/")
+public class SecondApp extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(ReloaderResource.class);
+        }};
+    }
+}
diff --git a/tests/integration/ejb-multimodule-reload/war2/src/main/resources/META-INF/beans.xml b/tests/integration/ejb-multimodule-reload/war2/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war2/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/ejb-multimodule-reload/war2/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web2/JaxRsFromEjbLibraryTest.java b/tests/integration/ejb-multimodule-reload/war2/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web2/JaxRsFromEjbLibraryTest.java
new file mode 100644
index 0000000..7dc32da
--- /dev/null
+++ b/tests/integration/ejb-multimodule-reload/war2/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/reload/web2/JaxRsFromEjbLibraryTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.reload.web2;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+/**
+ * Test for EJB web application resources. The JAX-RS resources come from bundled EJB library jar.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy ../ear/target/ejb-multimodule-ear-*.ear
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class JaxRsFromEjbLibraryTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new SecondApp();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("ejb-multimodule-war").path("resources").build();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testRequestCountGetsIncremented() {
+        final int requestCount1 = _nextCount(target().path("counter"));
+
+        final int requestCount2 = _nextCount(target().path("counter"));
+        assertThat(requestCount2, is(greaterThan(requestCount1)));
+
+        final int requestCount3 = _nextCount(target().path("stateless"));
+        assertThat(requestCount3, is(greaterThan(requestCount2)));
+
+        final int requestCount4 = _nextCount(target().path("stateless"));
+        assertThat(requestCount4, is(greaterThan(requestCount3)));
+
+        final int requestCount5 = _nextCount(target().path("stateful").path("count"));
+        assertThat(requestCount5, is(greaterThan(requestCount4)));
+
+        final int requestCount6 = _nextCount(target().path("stateful").path("count"));
+        assertThat(requestCount6, is(greaterThan(requestCount5)));
+
+        final int requestCount7 = _nextCount(target().path("war-stateless"));
+        assertThat(requestCount7, is(greaterThan(requestCount6)));
+
+        final int requestCount8 = _nextCount(target().path("war-stateless"));
+        assertThat(requestCount8, is(greaterThan(requestCount7)));
+    }
+
+    private int _nextCount(final WebTarget target) throws NumberFormatException {
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), is(200));
+        return Integer.parseInt(response.readEntity(String.class));
+    }
+
+    @Test
+    public void testUriInfoInjection() {
+        _testPath(target().path("counter").path("one"), "counter/one");
+        _testPath(target().path("counter").path("two"), "counter/two");
+        _testPath(target().path("stateless").path("three"), "stateless/three");
+        _testPath(target().path("stateless").path("four"), "stateless/four");
+        _testPath(target().path("war-stateless").path("five"), "war-stateless/five");
+        _testPath(target().path("war-stateless").path("six"), "war-stateless/six");
+    }
+
+    private void _testPath(final WebTarget target, final String expectedResult) {
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), equalTo(expectedResult));
+    }
+}
diff --git a/tests/integration/ejb-multimodule/ear/pom.xml b/tests/integration/ejb-multimodule/ear/pom.xml
new file mode 100644
index 0000000..742f13d
--- /dev/null
+++ b/tests/integration/ejb-multimodule/ear/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-ear</artifactId>
+    <packaging>ear</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-ear</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-ear-plugin</artifactId>
+                <configuration>
+                    <version>6</version>
+                    <defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
+                    <modules>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>ejb-multimodule-war</artifactId>
+                        </webModule>
+                        <ejbModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>ejb-multimodule-lib</artifactId>
+                        </ejbModule>
+                    </modules>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>ejb-multimodule-war</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>ejb-multimodule-lib</artifactId>
+            <type>ejb</type>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/integration/ejb-multimodule/ear/src/main/application/META-INF/MANIFEST.MF b/tests/integration/ejb-multimodule/ear/src/main/application/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..59499bc
--- /dev/null
+++ b/tests/integration/ejb-multimodule/ear/src/main/application/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+
diff --git a/tests/integration/ejb-multimodule/lib/pom.xml b/tests/integration/ejb-multimodule/lib/pom.xml
new file mode 100644
index 0000000..916ead4
--- /dev/null
+++ b/tests/integration/ejb-multimodule/lib/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-lib</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-lib</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/EjbCounterResource.java b/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/EjbCounterResource.java
new file mode 100644
index 0000000..b1fcf20
--- /dev/null
+++ b/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/EjbCounterResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.lib;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ejb.Singleton;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Singleton EJB counter bean as a JAX-RS resource.
+ * The bean is for one published as a standalone JAX-RS resource
+ * and for two used to inject other EJB based JAX-RS resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+@Path("counter")
+public class EjbCounterResource {
+
+    final AtomicInteger counter = new AtomicInteger();
+
+    @Context UriInfo ui;
+
+    @GET
+    public int getCount() {
+        return counter.incrementAndGet();
+    }
+
+    @Path("{ui}")
+    @GET
+    public String getUi() {
+        return ui != null ? ui.getPath() : "UriInfo is null";
+    }
+}
diff --git a/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/StatefulResource.java b/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/StatefulResource.java
new file mode 100644
index 0000000..6250c2d
--- /dev/null
+++ b/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/StatefulResource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.lib;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateful;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * JAX-RS resource backed with a stateful EJB bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateful
+@Path("stateful")
+public class StatefulResource {
+
+    @EJB EjbCounterResource counter;
+
+    @GET
+    @Path("count")
+    public int getCount() {
+        return counter.getCount();
+    }
+}
diff --git a/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/StatelessResource.java b/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/StatelessResource.java
new file mode 100644
index 0000000..b349095
--- /dev/null
+++ b/tests/integration/ejb-multimodule/lib/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/lib/StatelessResource.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.lib;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * JAX-RS resource backed by a stateless EJB bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@Path("stateless")
+public class StatelessResource {
+
+    @EJB EjbCounterResource counter;
+    @Context UriInfo uriInfo;
+
+    @GET
+    public int getCount() {
+        return counter.getCount();
+    }
+
+    @GET
+    @Path("{uriInfo}")
+    public String getPath() {
+        return uriInfo != null ? uriInfo.getPath() : "uri info is null";
+    }
+}
diff --git a/tests/integration/ejb-multimodule/pom.xml b/tests/integration/ejb-multimodule/pom.xml
new file mode 100644
index 0000000..78a39c6
--- /dev/null
+++ b/tests/integration/ejb-multimodule/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>ejb-multimodule</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-integration-ejb-multimodule</name>
+
+    <description>
+        EJB Multi-Module
+    </description>
+
+    <modules>
+        <module>ear</module>
+        <module>lib</module>
+        <module>war</module>
+    </modules>
+</project>
diff --git a/tests/integration/ejb-multimodule/war/pom.xml b/tests/integration/ejb-multimodule/war/pom.xml
new file mode 100644
index 0000000..8c40b68
--- /dev/null
+++ b/tests/integration/ejb-multimodule/war/pom.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ejb-multimodule-war</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-ejb-multimodule-war</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ejb-multimodule-lib</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                    <!--archive>
+                      <manifestEntries>
+                        <Class-Path>ejb-lib-${project.version}.jar</Class-Path>
+                      </manifestEntries>
+                    </archive-->
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/ejb-multimodule/war/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/JaxRsConfiguration.java b/tests/integration/ejb-multimodule/war/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/JaxRsConfiguration.java
new file mode 100644
index 0000000..18ab4ce
--- /dev/null
+++ b/tests/integration/ejb-multimodule/war/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/JaxRsConfiguration.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.web1;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.tests.integration.multimodule.ejb.lib.EjbCounterResource;
+import org.glassfish.jersey.tests.integration.multimodule.ejb.lib.StatefulResource;
+import org.glassfish.jersey.tests.integration.multimodule.ejb.lib.StatelessResource;
+
+/**
+ * JAX-RS application resource configuration that includes only
+ * those JAX-RS components imported from EJB library jar.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("resources")
+public class JaxRsConfiguration extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(EjbCounterResource.class);
+            add(StatelessResource.class);
+            add(StatefulResource.class);
+            add(WarStatelessResource.class);
+        }};
+    }
+}
diff --git a/tests/integration/ejb-multimodule/war/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/WarStatelessResource.java b/tests/integration/ejb-multimodule/war/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/WarStatelessResource.java
new file mode 100644
index 0000000..008539d
--- /dev/null
+++ b/tests/integration/ejb-multimodule/war/src/main/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/WarStatelessResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.multimodule.ejb.web1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+
+import org.glassfish.jersey.tests.integration.multimodule.ejb.lib.EjbCounterResource;
+
+/**
+ * JAX-RS resource backed by a stateless EJB bean placed in WAR module.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Stateless
+@Path("war-stateless")
+public class WarStatelessResource {
+
+    @EJB EjbCounterResource counter;
+    @Context UriInfo uriInfo;
+
+    @GET
+    public int getCount() {
+        return counter.getCount();
+    }
+
+    @GET
+    @Path("{uriInfo}")
+    public String getPath() {
+        return uriInfo != null ? uriInfo.getPath() : "uri info is null";
+    }
+}
diff --git a/tests/integration/ejb-multimodule/war/src/main/webapp/index.jsp b/tests/integration/ejb-multimodule/war/src/main/webapp/index.jsp
new file mode 100644
index 0000000..d9c02e5
--- /dev/null
+++ b/tests/integration/ejb-multimodule/war/src/main/webapp/index.jsp
@@ -0,0 +1,31 @@
+<%--
+
+    Copyright (c) 2015, 2018 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
+
+--%>
+
+<%@page contentType="text/html" pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>JSP Page</title>
+    </head>
+    <body>
+        <h1>Hello World!</h1>
+    </body>
+</html>
diff --git a/tests/integration/ejb-multimodule/war/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/JaxRsFromEjbLibraryTest.java b/tests/integration/ejb-multimodule/war/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/JaxRsFromEjbLibraryTest.java
new file mode 100644
index 0000000..3eb6aa0
--- /dev/null
+++ b/tests/integration/ejb-multimodule/war/src/test/java/org/glassfish/jersey/tests/integration/multimodule/ejb/web1/JaxRsFromEjbLibraryTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.multimodule.ejb.web1;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+/**
+ * Test for EJB web application resources. The JAX-RS resources come from bundled EJB library jar.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy ../ear/target/ejb-multimodule-ear-*.ear
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class JaxRsFromEjbLibraryTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsConfiguration();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("ejb-multimodule-war").path("resources").build();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testRequestCountGetsIncremented() {
+        final int requestCount1 = _nextCount(target().path("counter"));
+
+        final int requestCount2 = _nextCount(target().path("counter"));
+        assertThat(requestCount2, is(greaterThan(requestCount1)));
+
+        final int requestCount3 = _nextCount(target().path("stateless"));
+        assertThat(requestCount3, is(greaterThan(requestCount2)));
+
+        final int requestCount4 = _nextCount(target().path("stateless"));
+        assertThat(requestCount4, is(greaterThan(requestCount3)));
+
+        final int requestCount5 = _nextCount(target().path("stateful").path("count"));
+        assertThat(requestCount5, is(greaterThan(requestCount4)));
+
+        final int requestCount6 = _nextCount(target().path("stateful").path("count"));
+        assertThat(requestCount6, is(greaterThan(requestCount5)));
+
+        final int requestCount7 = _nextCount(target().path("war-stateless"));
+        assertThat(requestCount7, is(greaterThan(requestCount6)));
+
+        final int requestCount8 = _nextCount(target().path("war-stateless"));
+        assertThat(requestCount8, is(greaterThan(requestCount7)));
+    }
+
+    private int _nextCount(final WebTarget target) throws NumberFormatException {
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), is(200));
+        return Integer.parseInt(response.readEntity(String.class));
+    }
+
+    @Test
+    public void testUriInfoInjection() {
+        _testPath(target().path("counter").path("one"), "counter/one");
+        _testPath(target().path("counter").path("two"), "counter/two");
+        _testPath(target().path("stateless").path("three"), "stateless/three");
+        _testPath(target().path("stateless").path("four"), "stateless/four");
+        _testPath(target().path("war-stateless").path("five"), "war-stateless/five");
+        _testPath(target().path("war-stateless").path("six"), "war-stateless/six");
+    }
+
+    private void _testPath(final WebTarget target, final String expectedResult) {
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), equalTo(expectedResult));
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/pom.xml b/tests/integration/ejb-test-webapp/pom.xml
new file mode 100644
index 0000000..6ca69fa
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>ejb-test-webapp</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-ejb-webapp</name>
+
+    <description>Jersey EJB test web application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>3.1-b07</version>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+              <exclusion>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-core</artifactId>
+              </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+          <groupId>org.hamcrest</groupId>
+          <artifactId>hamcrest-library</artifactId>
+          <scope>test</scope>
+        </dependency>
+   </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AppResource.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AppResource.java
new file mode 100644
index 0000000..ba03d17
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AppResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.ejb.resources;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Test resource that exposes counter from the JAX-RS application subclass.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@Path("app")
+public class AppResource {
+
+    @EJB MyApplication app;
+
+    @Path("count")
+    @GET
+    public int getCount() {
+        return app.incrementAndGetCount();
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AsyncResource.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AsyncResource.java
new file mode 100644
index 0000000..e5c67fb
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AsyncResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+import javax.ejb.EJB;
+
+/**
+ * @author Jan Algermissen
+ * @author Miroslav Fuksa
+ */
+@Path("async-test")
+public class AsyncResource {
+
+    @EJB
+    AsyncService asyncService;
+
+    @GET
+    @Path("sync")
+    public String synchronousGet() {
+        return "sync";
+    }
+
+    @GET
+    @Path("async")
+    public void asynchronousGet(@Suspended AsyncResponse ar) {
+        asyncService.getAsync(ar);
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AsyncService.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AsyncService.java
new file mode 100644
index 0000000..8ea1ac4
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/AsyncService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import java.util.logging.Logger;
+
+import javax.ejb.Asynchronous;
+import javax.ejb.Stateless;
+import javax.ws.rs.container.AsyncResponse;
+
+/**
+ * @author Jan Algermissen
+ * @author Miroslav Fuksa
+ */
+@Stateless
+public class AsyncService {
+    private static Logger LOG = Logger.getLogger(AsyncService.class.getName());
+
+    @Asynchronous
+    public void getAsync(AsyncResponse ar) {
+        ar.resume("async");
+    }
+}
+
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CounterBean.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CounterBean.java
new file mode 100644
index 0000000..3e43e59
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CounterBean.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.ejb.Singleton;
+
+/**
+ * EJB singleton utilized as request counter in this test application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+public class CounterBean {
+
+    AtomicInteger counter = new AtomicInteger();
+
+    public int incrementAndGet() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CounterFilter.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CounterFilter.java
new file mode 100644
index 0000000..e67edc4
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CounterFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import java.io.IOException;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Response filter implemented as EJB bean. The filter adds Request-Count response header to each response.
+ * Another EJB singleton bean, CounterBean, is injected that holds the actual request count.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@Stateless
+public class CounterFilter implements ContainerResponseFilter{
+
+    public static final String RequestCountHEADER = "Request-Count";
+
+    @EJB CounterBean counter;
+
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        responseContext.getHeaders().add(RequestCountHEADER, counter.incrementAndGet());
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomBaseException.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomBaseException.java
new file mode 100644
index 0000000..fda6ed4
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomBaseException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+/**
+ * Custom exception. Part of JERSEY-2320 reproducer.
+ * This one serves as a base for other exceptions
+ * mapped by {@link EjbExceptionMapperOne} and {@link EjbExceptionMapperTwo}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomBaseException extends Exception {
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomExceptionOne.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomExceptionOne.java
new file mode 100644
index 0000000..8a71b6e
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomExceptionOne.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+/**
+ * Custom exception. Part of JERSEY-2320 reproducer.
+ * This one gets mapped by {@link EjbExceptionMapperOne}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomExceptionOne extends CustomBaseException {
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomExceptionTwo.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomExceptionTwo.java
new file mode 100644
index 0000000..021dbdc
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/CustomExceptionTwo.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+/**
+ * Custom exception. Part of JERSEY-2320 reproducer.
+ * This one gets mapped by {@link EjbExceptionMapperTwo}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomExceptionTwo extends CustomBaseException {
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/Echo.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/Echo.java
new file mode 100644
index 0000000..1e544bb
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/Echo.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import javax.ejb.Remote;
+
+/**
+ * EJB remote interface. Part of the reproducer for GLASSFISH-16199.
+ * See also the other test case implemented by {@link RawEchoResource}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Remote
+public interface  Echo {
+
+    String echo(String message);
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EchoBean.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EchoBean.java
new file mode 100644
index 0000000..7763ed8
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EchoBean.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import javax.ejb.Stateful;
+
+/**
+ * Session bean capable of returning an echoed message back.
+ * This is to prove EJB container is used in {@link EchoResource}
+ * and {@link RawEchoResource} resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateful
+public class EchoBean {
+
+    /**
+     * Prefix, {@value}, to be attached to each message processed by this bean.
+     */
+    public static final String PREFIX = "ECHOED: ";
+
+    /**
+     * Echo message.
+     *
+     * @param message to be echoed.
+     * @return incoming message prefixed with {@link #PREFIX}.
+     */
+    public String echo(final String message) {
+        return String.format("%s%s", PREFIX, message);
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EchoResource.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EchoResource.java
new file mode 100644
index 0000000..1414e32
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EchoResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import javax.ejb.EJB;
+import javax.ejb.Local;
+import javax.ejb.Remote;
+import javax.ejb.Stateless;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * JAX-RS resource bean backed by an EJB session bean
+ * implementing EJB interface that is annotated with both {@link Local}
+ * and {@link Remote} annotations.
+ * Reproducible test case for GLASSFISH-16199.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@Path("echo")
+public class EchoResource implements Echo {
+
+    @EJB EchoBean echoService;
+
+    @GET
+    @Override
+    public String echo(@QueryParam("message") String message) {
+        return echoService.echo(message);
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperBase.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperBase.java
new file mode 100644
index 0000000..715d481
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperBase.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+import javax.ws.rs.ext.ExceptionMapper;
+
+/**
+ * JERSEY-2320 reproducer. {@link CustomBaseException} will get mapped
+ * to an ordinary response.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public abstract class EjbExceptionMapperBase<T extends CustomBaseException> implements ExceptionMapper<T> {
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperOne.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperOne.java
new file mode 100644
index 0000000..0b9a559
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperOne.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+import javax.ejb.EJB;
+import javax.ejb.Singleton;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * JERSEY-2320 reproducer. {@link CustomExceptionOne} will get mapped
+ * to an ordinary response. We make sure the mapper gets injected properly
+ * by both Jersey runtime and EJB container.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+public class EjbExceptionMapperOne extends EjbExceptionMapperBase<CustomExceptionOne> {
+
+    public static final String RESPONSE_BODY = "custom exception one thrown";
+
+    @Context UriInfo uriInfo;
+    @EJB EchoBean echoBean;
+
+    @Override
+    public Response toResponse(final CustomExceptionOne exception) {
+        return Response.ok(RESPONSE_BODY)
+                .header("My-Location", uriInfo.getPath())
+                .header("My-Echo", echoBean.echo("1")).build();
+    }
+
+
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperTwo.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperTwo.java
new file mode 100644
index 0000000..61227e9
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/EjbExceptionMapperTwo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * JERSEY-2320 reproducer. {@link CustomExceptionTwo} will get mapped
+ * to an ordinary response. We make sure the mapper gets injected properly
+ * by both Jersey runtime and EJB container.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+public class EjbExceptionMapperTwo extends EjbExceptionMapperBase<CustomExceptionTwo> {
+
+    @Context UriInfo uriInfo;
+    @EJB EchoBean echoBean;
+
+    public static final String RESPONSE_BODY = "custom exception two thrown";
+
+    @Override
+    public Response toResponse(final CustomExceptionTwo exception) {
+        return Response.ok(RESPONSE_BODY)
+                .header("My-Location", uriInfo.getPath())
+                .header("My-Echo", echoBean.echo("2")).build();
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/ExceptionEjbResource.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/ExceptionEjbResource.java
new file mode 100644
index 0000000..9a12454
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/ExceptionEjbResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import javax.ejb.EJBException;
+import javax.ejb.Singleton;
+
+/**
+ * EJB backed JAX-RS resource to test if a custom exception info makes it to the client.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Singleton
+@Path("exception")
+public class ExceptionEjbResource {
+
+    public static class MyCheckedException extends Exception {
+
+        public MyCheckedException(String message) {
+            super(message);
+        }
+    }
+
+    public static class MyRuntimeException extends RuntimeException {
+
+        public MyRuntimeException(String message) {
+            super(message);
+        }
+    }
+
+    public static final String EjbExceptionMESSAGE = "ejb exception thrown directly";
+    public static final String CheckedExceptionMESSAGE = "checked exception thrown directly";
+
+    @GET
+    @Path("ejb")
+    public String throwEjbException() {
+        throw new EJBException(EjbExceptionMESSAGE);
+    }
+
+    @GET
+    @Path("checked")
+    public String throwCheckedException() throws MyCheckedException {
+        throw new MyCheckedException(CheckedExceptionMESSAGE);
+    }
+
+    @GET
+    @Path("custom1/{p}")
+    public String throwCustomExceptionOne() throws CustomBaseException {
+        throw new CustomExceptionOne();
+    }
+
+    @GET
+    @Path("custom2/{p}")
+    public String throwCustomExceptionTwo() throws CustomBaseException {
+        throw new CustomExceptionTwo();
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/MyApplication.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/MyApplication.java
new file mode 100644
index 0000000..c9c84a0
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/MyApplication.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ejb.Singleton;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/rest")
+@Singleton
+public class MyApplication extends Application {
+
+    private AtomicInteger counter;
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return new HashMap<String, Object>() {{
+            put("jersey.config.server.response.setStatusOverSendError", true);
+        }};
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+
+        this.counter = new AtomicInteger();
+
+        return new HashSet<Class<?>>() {{
+            add(AppResource.class);
+            add(ExceptionEjbResource.class);
+            add(EchoResource.class);
+            add(RawEchoResource.class);
+            add(CounterFilter.class);
+            add(AsyncResource.class);
+            add(EjbExceptionMapperOne.class);
+            add(EjbExceptionMapperTwo.class);
+        }};
+    }
+
+    public int incrementAndGetCount() {
+        return counter.incrementAndGet();
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/RawEcho.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/RawEcho.java
new file mode 100644
index 0000000..5b53347
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/RawEcho.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014, 2018 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.ejb.resources;
+
+/**
+ * Part of the reproducer for GLASSFISH-16199. This EJB business interface
+ * is being registered by an annotation on the EJB component class, {@link RawEchoResource}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface  RawEcho {
+
+    String echo(String message);
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/RawEchoResource.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/RawEchoResource.java
new file mode 100644
index 0000000..bbbe77c
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/RawEchoResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import javax.ejb.EJB;
+import javax.ejb.Remote;
+import javax.ejb.Stateless;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+/**
+ * JAX-RS resource bean backed by an EJB session bean
+ * implementing EJB interface, {@link RawEcho}, that is registered using {@link Remote} annotations.
+ * Reproducible test case for GLASSFISH-16199.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@Path("raw-echo")
+@Remote(RawEcho.class)
+public class RawEchoResource {
+
+    @EJB EchoBean echoService;
+
+    @GET
+    public String echo(@QueryParam("message") String message) {
+        return echoService.echo(message);
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/StandaloneServlet.java b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/StandaloneServlet.java
new file mode 100644
index 0000000..29e9ad5
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/main/java/org/glassfish/jersey/tests/ejb/resources/StandaloneServlet.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import java.io.IOException;
+
+import javax.ejb.EJB;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Standalone Servlet instance that has nothing to do with Jersey.
+ * It helps to compare Jersey and non-Jersey specific exception handling
+ * processing.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@WebServlet(name = "StandaloneServlet", urlPatterns = {"/servlet"})
+public class StandaloneServlet extends HttpServlet {
+
+    static final String ThrowCheckedExceptionACTION = "throwCheckedException";
+    static final String ThrowEjbExceptionACTION = "throwEjbException";
+
+    @EJB ExceptionEjbResource ejbResource;
+
+    /**
+     * Handles the HTTP <code>GET</code> method.
+     *
+     * @param request servlet request.
+     * @param response servlet response.
+     * @throws ServletException if a servlet-specific error occurs.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+
+        final String action = request.getParameter("action");
+
+        if (ThrowCheckedExceptionACTION.equals(action)) {
+            try {
+                ejbResource.throwCheckedException();
+            } catch (ExceptionEjbResource.MyCheckedException ex) {
+                throw new ServletException(ex);
+            }
+        }
+
+        if (ThrowEjbExceptionACTION.equals(action)) {
+            ejbResource.throwEjbException();
+        }
+
+        sayHello(response);
+    }
+
+    private void sayHello(HttpServletResponse response) throws IOException {
+        response.setHeader("Content-type", "text/plain");
+        response.getOutputStream().print(
+                String.format("Use action parameter to specify exception."
+                + " \nSupported options: %s, %s.", ThrowCheckedExceptionACTION, ThrowEjbExceptionACTION));
+    }
+}
diff --git a/tests/integration/ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/ejb/resources/EjbTest.java b/tests/integration/ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/ejb/resources/EjbTest.java
new file mode 100644
index 0000000..a588cf6
--- /dev/null
+++ b/tests/integration/ejb-test-webapp/src/test/java/org/glassfish/jersey/tests/ejb/resources/EjbTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2013, 2018 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.ejb.resources;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
+
+/**
+ * Test for EJB web application resources.
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy target/ejb-test-webapp
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class EjbTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("ejb-test-webapp").build();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(LoggingFeature.class);
+    }
+
+    @Test
+    public void testEjbException() {
+        final Response jerseyResponse = target().path("rest/exception/ejb").request().get();
+        _check500Response(jerseyResponse, ExceptionEjbResource.EjbExceptionMESSAGE);
+
+        final Response servletResponse =
+                target().path("servlet")
+                  .queryParam("action", StandaloneServlet.ThrowEjbExceptionACTION).request().get();
+        _check500Response(servletResponse, ExceptionEjbResource.EjbExceptionMESSAGE);
+    }
+
+    @Test
+    public void testCheckedException() {
+        final Response jerseyResponse = target().path("rest/exception/checked").request().get();
+        _check500Response(jerseyResponse, ExceptionEjbResource.CheckedExceptionMESSAGE);
+
+        final Response servletResponse =
+                target().path("servlet")
+                  .queryParam("action", StandaloneServlet.ThrowCheckedExceptionACTION).request().get();
+        _check500Response(servletResponse, ExceptionEjbResource.CheckedExceptionMESSAGE);
+    }
+
+    @Test
+    public void testCustomException1() {
+        Response jerseyResponse = target().path("rest/exception/custom1/big").request().get();
+        assertThat(jerseyResponse.getStatus(), is(200));
+        assertThat(jerseyResponse.readEntity(String.class), is(EjbExceptionMapperOne.RESPONSE_BODY));
+        assertThat(jerseyResponse.getHeaderString("My-Location"), is("exception/custom1/big"));
+        assertThat(jerseyResponse.getHeaderString("My-Echo"), is("ECHOED: 1"));
+
+        jerseyResponse = target().path("rest/exception/custom1/one").request().get();
+        assertThat(jerseyResponse.getStatus(), is(200));
+        assertThat(jerseyResponse.readEntity(String.class), is(EjbExceptionMapperOne.RESPONSE_BODY));
+        assertThat(jerseyResponse.getHeaderString("My-Location"), is("exception/custom1/one"));
+        assertThat(jerseyResponse.getHeaderString("My-Echo"), is("ECHOED: 1"));
+    }
+
+    @Test
+    public void testCustomException2() {
+        Response jerseyResponse = target().path("rest/exception/custom2/small").request().get();
+        assertThat(jerseyResponse.getStatus(), is(200));
+        assertThat(jerseyResponse.readEntity(String.class), is(EjbExceptionMapperTwo.RESPONSE_BODY));
+        assertThat(jerseyResponse.getHeaderString("My-Location"), is("exception/custom2/small"));
+        assertThat(jerseyResponse.getHeaderString("My-Echo"), is("ECHOED: 2"));
+
+        jerseyResponse = target().path("rest/exception/custom2/one").request().get();
+        assertThat(jerseyResponse.getStatus(), is(200));
+        assertThat(jerseyResponse.readEntity(String.class), is(EjbExceptionMapperTwo.RESPONSE_BODY));
+        assertThat(jerseyResponse.getHeaderString("My-Location"), is("exception/custom2/one"));
+        assertThat(jerseyResponse.getHeaderString("My-Echo"), is("ECHOED: 2"));
+    }
+
+    @Test
+    public void testRemoteLocalEJBInterface() {
+
+        final String message = "Hi there";
+        final Response response = target().path("rest/echo").queryParam("message", message).request().get();
+
+        assertThat(response.getStatus(), is(200));
+
+        final String responseMessage = response.readEntity(String.class);
+
+        assertThat(responseMessage, startsWith(EchoBean.PREFIX));
+        assertThat(responseMessage, endsWith(message));
+    }
+
+    @Test
+    public void testRemoteAnnotationRegisteredEJBInterface() {
+
+        final String message = "Hi there";
+        final Response response = target().path("rest/raw-echo").queryParam("message", message).request().get();
+
+        assertThat(response.getStatus(), is(200));
+
+        final String responseMessage = response.readEntity(String.class);
+
+        assertThat(responseMessage, startsWith(EchoBean.PREFIX));
+        assertThat(responseMessage, endsWith(message));
+    }
+
+    @Test
+    public void testRequestCountGetsIncremented() {
+
+        final Response response1 = target().path("rest/echo").queryParam("message", "whatever").request().get();
+        assertThat(response1.getStatus(), is(200));
+        final String counterHeader1 = response1.getHeaderString(CounterFilter.RequestCountHEADER);
+        final int requestCount1 = Integer.parseInt(counterHeader1);
+
+        final Response response2 = target().path("rest/echo").queryParam("message", requestCount1).request().get();
+        assertThat(response2.getStatus(), is(200));
+        final int requestCount2 = Integer.parseInt(response2.getHeaderString(CounterFilter.RequestCountHEADER));
+
+        assertThat(requestCount2, is(greaterThan(requestCount1)));
+    }
+
+
+    @Test
+    public void testSync() {
+        final Response response = target().path("rest/async-test/sync").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("sync"));
+    }
+
+    @Test
+    public void testAsync() {
+        final Response response = target().path("rest/async-test/async").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("async"));
+    }
+
+    @Test
+    public void testAppIsEjbSingleton() {
+
+        int c1 = target().path("rest/app/count").request().get(Integer.class);
+        int c2 = target().path("rest/app/count").request().get(Integer.class);
+        int c3 = target().path("rest/app/count").request().get(Integer.class);
+
+        assertThat("the first count should be less than the second one", c1, is(lessThan(c2)));
+        assertThat("the second count should be less than the third one", c2, is(lessThan(c3)));
+    }
+
+    private void _check500Response(final Response response, final String expectedSubstring) {
+        assertThat(response.getStatus(), is(500));
+        assertThat(response.readEntity(String.class), containsString(expectedSubstring));
+    }
+}
diff --git a/tests/integration/j-376/pom.xml b/tests/integration/j-376/pom.xml
new file mode 100644
index 0000000..4908e7c
--- /dev/null
+++ b/tests/integration/j-376/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>j-376</artifactId>
+    <packaging>jar</packaging>
+    <name>j-376-reproducer</name>
+
+    <description>Jersey test web application - J-376 reproducer</description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <scope>compile</scope>
+            <version>1.1.0.Final</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-weld2-se</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext.cdi</groupId>
+            <artifactId>jersey-cdi1x</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-server</artifactId>
+            <version>2.3.16</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.tests.integration.j376.GrizzlyApp</mainClass>
+                    <classpathScope>test</classpathScope>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ApplicationScopedBean.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ApplicationScopedBean.java
new file mode 100644
index 0000000..1f5e78e
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ApplicationScopedBean.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+
+/**
+ * Test {@code ApplicationScoped} bean to be injected to the test resource, while another {@code RequestScoped}
+ * bean being injected into this class.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@ApplicationScoped
+public class ApplicationScopedBean {
+
+    /** Request scoped bean injected via CDI */
+    @Inject
+    private SecondBean bean;
+
+    /** JAX-RS {@code Context} injection of request {@code UriInfo} */
+    @Context
+    private UriInfo uri;
+
+    private String name = "ApplicationScopedBean";
+
+    public String getMessage() {
+        return name + ":" + bean.getMessage();
+    }
+
+    public String getUri() {
+        return uri.getPath();
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ApplicationScopedResource.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ApplicationScopedResource.java
new file mode 100644
index 0000000..b0bf055
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ApplicationScopedResource.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Resource to test CDI injection into {@code ApplicationScoped} JAX-RS resource.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("appScope")
+@ApplicationScoped
+public class ApplicationScopedResource {
+
+    /** Application scoped CDI injected bean */
+    @Inject
+    private ApplicationScopedBean appScoped;
+
+    /** Request scoped CDI injected bean */
+    @Inject
+    private SecondBean reqScoped;
+
+    /** Bean containing form parameters injected by JAX-RS */
+    @Inject
+    @BeanParam
+    private FormDataBean bean;
+
+    @POST
+    @Produces("text/plain")
+    public String get() {
+        return bean.getName() + ":" + bean.getAge() + ":"
+                + bean.getInjectedBean().getMessage() + ":" + bean.getInjectedPath();
+    }
+
+    @GET
+    @Path("msg")
+    @Produces("text/plain")
+    public String testAppScoped() {
+        return appScoped.getMessage();
+    }
+
+    @GET
+    @Path("uri")
+    public String getUri() {
+        return appScoped.getUri();
+    }
+
+    @GET
+    @Path("req")
+    public String getMessage() {
+        return reqScoped.getMessage();
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ConstructorInjectionResource.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ConstructorInjectionResource.java
new file mode 100644
index 0000000..a016733
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/ConstructorInjectionResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.validation.Valid;
+
+/**
+ * Resource to test CDI injection into JAX-RS resource via constructor parameter.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("constructor")
+@RequestScoped
+public class ConstructorInjectionResource {
+
+    private FormDataBean bean;
+
+    public ConstructorInjectionResource() {
+    }
+
+    @Inject
+    public ConstructorInjectionResource(@Valid @BeanParam final FormDataBean form) {
+        bean = form;
+    }
+
+    @POST
+    @Produces("text/plain")
+    public String get() {
+        return bean.getName() + ":" + bean.getAge() + ":"
+                + bean.getInjectedBean().getMessage() + ":" + bean.getInjectedPath();
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/FieldInjectionResource.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/FieldInjectionResource.java
new file mode 100644
index 0000000..84c6ec5
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/FieldInjectionResource.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.validation.Valid;
+import javax.ws.rs.BeanParam;
+
+/**
+ * Resource to test CDI injection into JAX-RS resource via field.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("field")
+@RequestScoped
+public class FieldInjectionResource {
+
+    /** CDI injected request scoped field */
+    @Inject
+    @Valid
+    @BeanParam
+    private FormDataBean bean;
+
+    /** CDI injected applciation scoped bean */
+    @Inject
+    private ApplicationScopedBean appScoped;
+
+    /**
+     * Return string containing of fields from the injected non JAX-RS request scoped bean,
+     * path injected into it via {@code Context} annotation and another bean injected into it.
+     *
+     * Shows, that {@code Inject} and {@code Context} annotations can be used on one particular non JAX-RS class.
+     **/
+    @POST
+    @Produces("text/plain")
+    public String get() {
+        return bean.getName() + ":" + bean.getAge() + ":"
+                + bean.getInjectedBean().getMessage() + ":" + bean.getInjectedPath();
+    }
+
+    /** Return string from the {@code ApplicationScoped} non JAX_RS bean injected into this JAX-RS resource. */
+    @GET
+    @Path("appScoped")
+    @Produces("text/plain")
+    public String getMessage() {
+        return appScoped.getMessage();
+    }
+
+    /**
+     * Return path injected via {@code Context} annotation into {@code ApplicationScoped} non JAX-RS bean, that is
+     * further injected into this JAX-RS resource via CDI.
+     */
+    @GET
+    @Path("appScopedUri")
+    public String getUri() {
+        return appScoped.getUri();
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/FormDataBean.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/FormDataBean.java
new file mode 100644
index 0000000..819c498
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/FormDataBean.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * Test bean containingboth JAX-RS and CDI injection points.
+ */
+@RequestScoped
+public class FormDataBean {
+
+    private String injectedPath = null;
+
+    @NotNull
+    @Size(min = 4)
+    @FormParam("name")
+    private String name;
+
+    @Min(18)
+    @FormParam("age")
+    private int age;
+
+    @Inject
+    private SecondBean injectedBean;
+
+    @Context
+    private UriInfo uri;
+
+    public String getName() {
+        return name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    /**
+     * Exposes the state of injected {@code UriInfo} in the time of the call of {@link javax.annotation.PostConstruct}
+     * annotated method. The returned value will be used in test to ensure, that {@code UriInfo} is injected in time
+     *
+     * @return path injected via {@code UriInfo} at the time-point of the {@link #postConstruct()} method call.
+
+     */
+    public String getInjectedPath() {
+        return injectedPath;
+    }
+
+    public SecondBean getInjectedBean() {
+        return injectedBean;
+    }
+
+    @PostConstruct
+    public void postConstruct() {
+        this.injectedPath = uri.getPath();
+    }
+
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/GrizzlyApp.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/GrizzlyApp.java
new file mode 100644
index 0000000..3de6d9c
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/GrizzlyApp.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.jboss.weld.environment.se.Weld;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * CDI Test App launcher. Starts the Grizzly server and initializes weld.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class GrizzlyApp {
+
+    private static Weld weld;
+    private static HttpServer server;
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/j376/");
+
+    public static void main(String[] args) {
+        try {
+            System.out.println("Jersey CDI Test App");
+
+            start();
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nHit enter to stop it...",
+                    BASE_URI, "application.wadl"));
+            System.in.read();
+            stop();
+        } catch (IOException ex) {
+            Logger.getLogger(GrizzlyApp.class.getName()).log(Level.SEVERE, null, ex);
+        }
+
+    }
+
+    protected static void stop() {
+        server.shutdownNow();
+        weld.shutdown();
+    }
+
+    protected static void start() {
+        weld = new Weld();
+        weld.initialize();
+
+        server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createJaxRsApp(), true);
+    }
+
+    public static URI getBaseUri() {
+        return BASE_URI;
+    }
+
+    public static ResourceConfig createJaxRsApp() {
+        return new ResourceConfig(new MyApplication().getClasses());
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/MethodInjectionResource.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/MethodInjectionResource.java
new file mode 100644
index 0000000..3f19676
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/MethodInjectionResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.validation.Valid;
+
+/**
+ * Resource to test CDI injection into JAX-RS resource via setter.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("method")
+@RequestScoped
+public class MethodInjectionResource {
+
+    private FormDataBean bean;
+
+    @Inject
+    public void setFormDataBean(@Valid @BeanParam final FormDataBean form) {
+        bean = form;
+    }
+
+    @POST
+    @Produces("text/plain")
+    public String get() {
+        return bean.getName() + ":" + bean.getAge() + ":"
+                + bean.getInjectedBean().getMessage() + ":" + bean.getInjectedPath();
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/MyApplication.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/MyApplication.java
new file mode 100644
index 0000000..4ec0beb
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/MyApplication.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * JAX-RS Application subclass, defines the test application resources.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@ApplicationPath("/*")
+public class MyApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<>();
+        classes.add(MethodInjectionResource.class);
+        classes.add(ConstructorInjectionResource.class);
+        classes.add(FieldInjectionResource.class);
+        classes.add(ApplicationScopedResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/SecondBean.java b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/SecondBean.java
new file mode 100644
index 0000000..2e5ead4
--- /dev/null
+++ b/tests/integration/j-376/src/main/java/org/glassfish/jersey/tests/integration/j376/SecondBean.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Bean to be injected into another bean by CDI.
+ *
+ * The purpose is to test, that CDI and hk2 injections are working together so that one class be injected by
+ * both CDI and Jersey/hk2.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RequestScoped
+public class SecondBean {
+    private String message = "Hello";
+
+    public String getMessage() {
+        return message;
+    }
+
+}
diff --git a/tests/integration/j-376/src/main/resources/META-INF/beans.xml b/tests/integration/j-376/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..3b46d69
--- /dev/null
+++ b/tests/integration/j-376/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/j-376/src/test/java/org/glassfish/jersey/tests/integration/j376/J376Test.java b/tests/integration/j-376/src/test/java/org/glassfish/jersey/tests/integration/j376/J376Test.java
new file mode 100644
index 0000000..fa39021
--- /dev/null
+++ b/tests/integration/j-376/src/test/java/org/glassfish/jersey/tests/integration/j376/J376Test.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j376;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class J376Test {
+    final Client client = ClientBuilder.newClient();
+    final WebTarget target = client.target(GrizzlyApp.getBaseUri());
+
+    @BeforeClass
+    public static void setUpTest() {
+        GrizzlyApp.start();
+    }
+
+    @AfterClass
+    public static void tearDownTest() {
+        GrizzlyApp.stop();
+    }
+
+    @Test
+    public void testConstructorInjection() {
+        final String response = target.path("constructor").request().post(Entity.entity("name=John&age=32",
+                MediaType.APPLICATION_FORM_URLENCODED_TYPE), String.class);
+
+        assertEquals("John:32:Hello:constructor", response);
+    }
+
+    @Test
+    public void testFieldInjection() {
+        final String response = target.path("field").request().post(Entity.entity("name=Bill&age=21",
+                MediaType.APPLICATION_FORM_URLENCODED_TYPE), String.class);
+
+        assertEquals("Bill:21:Hello:field", response);
+    }
+
+    @Test
+    public void testMethodInjection() {
+        final String response = target.path("method").request().post(Entity.entity("name=Mike&age=42",
+                MediaType.APPLICATION_FORM_URLENCODED_TYPE), String.class);
+
+        assertEquals("Mike:42:Hello:method", response);
+    }
+
+    @Test
+    public void testAppScopedBeanInReqScopedResource() {
+        final String response = target.path("field/appScoped").request().get(String.class);
+        assertEquals("ApplicationScopedBean:Hello", response);
+    }
+
+    @Test
+    public void testAppScopedResource() {
+        String response = target.path("appScope/msg").request().get(String.class);
+        assertEquals("ApplicationScopedBean:Hello", response);
+        response = target.path("appScope/uri").request().get(String.class);
+        assertEquals("appScope/uri", response);
+        response = target.path("appScope/req").request().get(String.class);
+        assertEquals("Hello", response);
+    }
+
+    @Test
+    public void testBeanParamInAppScoped() {
+        final String response = target.path("appScope").request().post(Entity.entity("name=John&age=35",
+                MediaType.APPLICATION_FORM_URLENCODED_TYPE), String.class);
+
+        assertEquals("John:35:Hello:appScope", response);
+    }
+
+    @Test
+    public void testContextInjectionInAppScopedBean() {
+        final String response = target.path("field/appScopedUri").request().get(String.class);
+        assertEquals("field/appScopedUri", response);
+
+    }
+}
diff --git a/tests/integration/j-441/ear/pom.xml b/tests/integration/j-441/ear/pom.xml
new file mode 100644
index 0000000..ffeb59f
--- /dev/null
+++ b/tests/integration/j-441/ear/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <artifactId>j-441-ear</artifactId>
+    <packaging>ear</packaging>
+
+    <name>jersey-tests-integration-j-441-ear</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-ear-plugin</artifactId>
+                <configuration>
+                    <version>6</version>
+                    <defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
+                    <modules>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>j-441-war1</artifactId>
+                            <contextRoot>/one</contextRoot>
+                        </webModule>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>j-441-war2</artifactId>
+                            <contextRoot>/two</contextRoot>
+                        </webModule>
+                    </modules>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>j-441-war1</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>j-441-war2</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/integration/j-441/pom.xml b/tests/integration/j-441/pom.xml
new file mode 100644
index 0000000..e8109bf
--- /dev/null
+++ b/tests/integration/j-441/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>j-441</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-integration-j-441</name>
+
+    <description>
+        J-441 Reproducer: Two WARs with enabled CDI in an EAR.
+    </description>
+
+    <modules>
+        <module>ear</module>
+        <module>war1</module>
+        <module>war2</module>
+    </modules>
+</project>
diff --git a/tests/integration/j-441/war1/pom.xml b/tests/integration/j-441/war1/pom.xml
new file mode 100644
index 0000000..067ef5e
--- /dev/null
+++ b/tests/integration/j-441/war1/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>j-441-war1</artifactId>
+    <packaging>war</packaging>
+
+    <name>jersey-tests-integration-j-441-war1</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/CdiResource.java b/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/CdiResource.java
new file mode 100644
index 0000000..33ce8da
--- /dev/null
+++ b/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/CdiResource.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.one;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.servlet.ServletContext;
+
+/**
+ * CDI backed JAX-RS resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/test")
+@RequestScoped
+public class CdiResource {
+
+    @Context
+    private ServletContext scField;
+
+    private ServletContext scCtorParam;
+
+    /* to make CDI happy */
+    public CdiResource() {
+    }
+
+    @Inject
+    public CdiResource(@Context final ServletContext sc) {
+        this.scCtorParam = sc;
+    }
+
+    @GET
+    @Path("ctor-param")
+    public String getCtorParam() {
+        return scCtorParam.getContextPath();
+    }
+
+    @GET
+    @Path("method-param")
+    public String getMethodParam(@Context final ServletContext sc) {
+        return sc.getContextPath();
+    }
+
+    @GET
+    @Path("field")
+    public String getField() {
+        return scField.getContextPath();
+    }
+
+    @GET
+    @Path("exception")
+    public String getException() throws Exception {
+        throw new Exception() {};
+    }
+}
diff --git a/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/CustomExceptionMapper.java b/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/CustomExceptionMapper.java
new file mode 100644
index 0000000..d5aa71c
--- /dev/null
+++ b/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/CustomExceptionMapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.one;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.servlet.ServletContext;
+
+/**
+ * JAX-RS provider added just to make sure the application
+ * deploys fine. Since JAX-RS providers get initialized
+ * at Jersey bootstrapping phase, we would get a deploy
+ * error if something went wrong.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomExceptionMapper implements ExceptionMapper<Exception> {
+
+    @Context
+    private ServletContext sc;
+
+    public Response toResponse(final Exception ex) {
+        return Response.status(200).entity(sc.getContextPath()).build();
+    }
+}
diff --git a/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/MyApplication.java b/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/MyApplication.java
new file mode 100644
index 0000000..073b84c
--- /dev/null
+++ b/tests/integration/j-441/war1/src/main/java/org/glassfish/jersey/tests/integration/j441/one/MyApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.one;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Test case to ensure proper WAR isolation on Jersey level.
+ * Define JAX-RS application containing a simple CDI backed JAX-RS resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        super(CdiResource.class, CustomExceptionMapper.class);
+    }
+}
diff --git a/tests/integration/j-441/war1/src/main/webapp/WEB-INF/beans.xml b/tests/integration/j-441/war1/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..93cfed7
--- /dev/null
+++ b/tests/integration/j-441/war1/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans xmlns="http://java.sun.com/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+</beans>
diff --git a/tests/integration/j-441/war1/src/test/java/org/glassfish/jersey/tests/integration/j441/one/ContextPathTest.java b/tests/integration/j-441/war1/src/test/java/org/glassfish/jersey/tests/integration/j441/one/ContextPathTest.java
new file mode 100644
index 0000000..285859e
--- /dev/null
+++ b/tests/integration/j-441/war1/src/test/java/org/glassfish/jersey/tests/integration/j441/one/ContextPathTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.one;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * This is to make sure two Jersey wars are separated well in a single ear.
+ *
+ * @author Michal Gajdos
+ */
+public class ContextPathTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testFieldInjection() {
+        assertThat(target("one/test/field").request().get(String.class), is("/one"));
+    }
+
+    @Test
+    public void testConstructorInjection() {
+        assertThat(target("one/test/ctor-param").request().get(String.class), is("/one"));
+    }
+
+    @Test
+    public void testMethodInjection() {
+        assertThat(target("one/test/method-param").request().get(String.class), is("/one"));
+    }
+
+    @Test
+    public void testExceptionMapperInjection() {
+        assertThat(target("one/test/exception").request().get(String.class), is("/one"));
+    }
+}
diff --git a/tests/integration/j-441/war2/pom.xml b/tests/integration/j-441/war2/pom.xml
new file mode 100644
index 0000000..cd385ad
--- /dev/null
+++ b/tests/integration/j-441/war2/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>j-441-war2</artifactId>
+    <packaging>war</packaging>
+
+    <name>jersey-tests-integration-j-441-war2</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/CdiResource.java b/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/CdiResource.java
new file mode 100644
index 0000000..0629273
--- /dev/null
+++ b/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/CdiResource.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.two;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.servlet.ServletContext;
+
+/**
+ * CDI backed JAX-RS resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/test")
+@RequestScoped
+public class CdiResource {
+
+    @Context
+    private ServletContext scField;
+
+    private ServletContext scCtorParam;
+
+    /* to make CDI happy */
+    public CdiResource() {
+    }
+
+    @Inject
+    public CdiResource(@Context final ServletContext sc) {
+        this.scCtorParam = sc;
+    }
+
+    @GET
+    @Path("ctor-param")
+    public String getCtorParam() {
+        return scCtorParam.getContextPath();
+    }
+
+    @GET
+    @Path("method-param")
+    public String getMethodParam(@Context final ServletContext sc) {
+        return sc.getContextPath();
+    }
+
+    @GET
+    @Path("field")
+    public String getField() {
+        return scField.getContextPath();
+    }
+
+    @GET
+    @Path("exception")
+    public String getException() throws Exception {
+        throw new Exception() {};
+    }
+}
diff --git a/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/CustomExceptionMapper.java b/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/CustomExceptionMapper.java
new file mode 100644
index 0000000..0544100
--- /dev/null
+++ b/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/CustomExceptionMapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.two;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import javax.servlet.ServletContext;
+
+/**
+ * JAX-RS provider added just to make sure the application
+ * deploys fine. Since JAX-RS providers get initialized
+ * at Jersey bootstrapping phase, we would get a deploy
+ * error if something went wrong.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class CustomExceptionMapper implements ExceptionMapper<Exception> {
+
+    @Context
+    private ServletContext sc;
+
+    public Response toResponse(final Exception ex) {
+        return Response.status(200).entity(sc.getContextPath()).build();
+    }
+}
diff --git a/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/MyApplication.java b/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/MyApplication.java
new file mode 100644
index 0000000..df6d3e3
--- /dev/null
+++ b/tests/integration/j-441/war2/src/main/java/org/glassfish/jersey/tests/integration/j441/two/MyApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.two;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Test case to ensure proper WAR isolation on Jersey level.
+ * Define JAX-RS application containing a simple CDI backed JAX-RS resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        super(CdiResource.class, CustomExceptionMapper.class);
+    }
+}
diff --git a/tests/integration/j-441/war2/src/main/webapp/WEB-INF/beans.xml b/tests/integration/j-441/war2/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..93cfed7
--- /dev/null
+++ b/tests/integration/j-441/war2/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<beans xmlns="http://java.sun.com/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+</beans>
diff --git a/tests/integration/j-441/war2/src/test/java/org/glassfish/jersey/tests/integration/j441/two/ContextPathTest.java b/tests/integration/j-441/war2/src/test/java/org/glassfish/jersey/tests/integration/j441/two/ContextPathTest.java
new file mode 100644
index 0000000..bb4f68b
--- /dev/null
+++ b/tests/integration/j-441/war2/src/test/java/org/glassfish/jersey/tests/integration/j441/two/ContextPathTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.j441.two;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * This is to make sure two Jersey wars are separated well in a single ear.
+ *
+ * @author Michal Gajdos
+ */
+public class ContextPathTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testFieldInjection() {
+        assertThat(target("two/test/field").request().get(String.class), is("/two"));
+    }
+
+    @Test
+    public void testConstructorInjection() {
+        assertThat(target("two/test/ctor-param").request().get(String.class), is("/two"));
+    }
+
+    @Test
+    public void testMethodInjection() {
+        assertThat(target("two/test/method-param").request().get(String.class), is("/two"));
+    }
+
+    @Test
+    public void testExceptionMapperInjection() {
+        assertThat(target("two/test/exception").request().get(String.class), is("/two"));
+    }
+}
diff --git a/tests/integration/j-59/ear/pom.xml b/tests/integration/j-59/ear/pom.xml
new file mode 100644
index 0000000..fdce31a
--- /dev/null
+++ b/tests/integration/j-59/ear/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>j-59-ear</artifactId>
+    <packaging>ear</packaging>
+    <name>jersey-tests-integration-j-59-ear</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-ear-plugin</artifactId>
+                <configuration>
+                    <version>6</version>
+                    <defaultLibBundleDir>APP-INF/lib</defaultLibBundleDir>
+                    <modules>
+                        <webModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>j-59-cdi-war</artifactId>
+                        </webModule>
+                        <ejbModule>
+                            <groupId>org.glassfish.jersey.tests.integration</groupId>
+                            <artifactId>j-59-ejb-lib</artifactId>
+                        </ejbModule>
+                    </modules>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>j-59-cdi-war</artifactId>
+            <type>war</type>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.tests.integration</groupId>
+            <artifactId>j-59-ejb-lib</artifactId>
+            <type>ejb</type>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/integration/j-59/lib/pom.xml b/tests/integration/j-59/lib/pom.xml
new file mode 100644
index 0000000..e90e520
--- /dev/null
+++ b/tests/integration/j-59/lib/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>j-59-ejb-lib</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-integration-j-59-ejb-lib</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/j-59/lib/src/main/java/org/glassfish/jersey/tests/integration/j59/ejb/lib/LocalBeanWithRemoteInterface.java b/tests/integration/j-59/lib/src/main/java/org/glassfish/jersey/tests/integration/j59/ejb/lib/LocalBeanWithRemoteInterface.java
new file mode 100644
index 0000000..086976a
--- /dev/null
+++ b/tests/integration/j-59/lib/src/main/java/org/glassfish/jersey/tests/integration/j59/ejb/lib/LocalBeanWithRemoteInterface.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.j59.ejb.lib;
+
+import javax.ejb.LocalBean;
+import javax.ejb.Remote;
+import javax.ejb.Stateless;
+
+/**
+ * Local stateless session bean implementing a remote interface.
+ * Part of CDI extension lookup issue reproducer.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+@LocalBean
+@Remote(MyRemoteInterface.class)
+public class LocalBeanWithRemoteInterface {
+
+    /**
+     * Simple getter to be invoked from a CDI backed JAX-RS resource.
+     *
+     * @return Josh string literal.
+     */
+    public String getName() {
+        return "Josh";
+    }
+}
diff --git a/tests/integration/j-59/lib/src/main/java/org/glassfish/jersey/tests/integration/j59/ejb/lib/MyRemoteInterface.java b/tests/integration/j-59/lib/src/main/java/org/glassfish/jersey/tests/integration/j59/ejb/lib/MyRemoteInterface.java
new file mode 100644
index 0000000..ff53484
--- /dev/null
+++ b/tests/integration/j-59/lib/src/main/java/org/glassfish/jersey/tests/integration/j59/ejb/lib/MyRemoteInterface.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.j59.ejb.lib;
+
+/**
+ * Marker interface for remote beans. Part of CDI extension lookup issue reproducer.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface MyRemoteInterface {
+}
diff --git a/tests/integration/j-59/pom.xml b/tests/integration/j-59/pom.xml
new file mode 100644
index 0000000..2512403
--- /dev/null
+++ b/tests/integration/j-59/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>j-59</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-integration-j-59</name>
+
+    <description>
+        J-59 Reproducer: CDI extension lookup in multi-module app to Jersey 2.
+    </description>
+
+    <modules>
+        <module>ear</module>
+        <module>lib</module>
+        <module>war</module>
+    </modules>
+</project>
diff --git a/tests/integration/j-59/war/pom.xml b/tests/integration/j-59/war/pom.xml
new file mode 100644
index 0000000..1783544
--- /dev/null
+++ b/tests/integration/j-59/war/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>j-59-cdi-war</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-j-59-cdi-war</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>j-59-ejb-lib</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/CdiBackedResource.java b/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/CdiBackedResource.java
new file mode 100644
index 0000000..f3483dd
--- /dev/null
+++ b/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/CdiBackedResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.j59.cdi.web;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import javax.jws.WebResult;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.tests.integration.j59.ejb.lib.LocalBeanWithRemoteInterface;
+
+/**
+ * Part of CDI extension lookup issue reproducer.
+ * This bean will CDI-inject a local EJB bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("name")
+@RequestScoped
+public class CdiBackedResource implements ResourceMarkerInterface {
+
+    @Inject
+    private LocalBeanWithRemoteInterface localBean;
+
+    @GET
+    @Path("hello")
+    @Produces(MediaType.TEXT_PLAIN)
+    @WebResult(name = "hello")
+    public String sayHello() {
+        return "Hello " + localBean.getName();
+    }
+}
diff --git a/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/JaxRsConfiguration.java b/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/JaxRsConfiguration.java
new file mode 100644
index 0000000..976125a
--- /dev/null
+++ b/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/JaxRsConfiguration.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.j59.cdi.web;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * CDI extension lookup issue reproducer application configuration.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("resources")
+public class JaxRsConfiguration extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(CdiBackedResource.class);
+        }};
+    }
+}
diff --git a/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/ResourceMarkerInterface.java b/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/ResourceMarkerInterface.java
new file mode 100644
index 0000000..f1548c6
--- /dev/null
+++ b/tests/integration/j-59/war/src/main/java/org/glassfish/jersey/tests/integration/j59/cdi/web/ResourceMarkerInterface.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.j59.cdi.web;
+
+/**
+ * Marker interface for CDI backed JAX-RS bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public interface ResourceMarkerInterface {
+}
diff --git a/tests/integration/j-59/war/src/main/webapp/WEB-INF/beans.xml b/tests/integration/j-59/war/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..6d5c2c1
--- /dev/null
+++ b/tests/integration/j-59/war/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<beans xmlns="http://java.sun.com/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+</beans>
diff --git a/tests/integration/j-59/war/src/main/webapp/index.jsp b/tests/integration/j-59/war/src/main/webapp/index.jsp
new file mode 100644
index 0000000..a59a2a4
--- /dev/null
+++ b/tests/integration/j-59/war/src/main/webapp/index.jsp
@@ -0,0 +1,29 @@
+<%@page contentType="text/html" pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+
+    Copyright (c) 2018 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
+
+-->
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>CDI Extension Lookup Issue Reproducer</title>
+    </head>
+    <body>
+        Visit <a href="resources/name/hello">resources/name/hello</a>
+    </body>
+</html>
diff --git a/tests/integration/j-59/war/src/test/java/org/glassfish/jersey/tests/integration/j59/cdi/web/NameBeanTest.java b/tests/integration/j-59/war/src/test/java/org/glassfish/jersey/tests/integration/j59/cdi/web/NameBeanTest.java
new file mode 100644
index 0000000..26870a9
--- /dev/null
+++ b/tests/integration/j-59/war/src/test/java/org/glassfish/jersey/tests/integration/j59/cdi/web/NameBeanTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.j59.cdi.web;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test for CDI extension lookup issue. Should the extension lookup have failed,
+ * we would not get the correct response here.
+ * <p/>
+ * Run with:
+ * <pre>
+ * mvn clean package
+ * $AS_HOME/bin/asadmin deploy ../ejb-jax-rs-ear1/target/ejb-jax-rs-ear1
+ * mvn -DskipTests=false test</pre>
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class NameBeanTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsConfiguration();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("j-59-cdi-war").path("resources").build();
+    }
+
+    @Test
+    public void testCdiResource() {
+        final Response r = target().path("name").path("hello").request().get();
+        final String content = r.readEntity(String.class);
+        assertThat(r.getStatus(), is(200));
+        assertThat(content, equalTo("Hello Josh"));
+    }
+}
diff --git a/tests/integration/jaxrs-component-inject/pom.xml b/tests/integration/jaxrs-component-inject/pom.xml
new file mode 100644
index 0000000..9a392ab
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jaxrs-component-inject</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-component-inject</name>
+
+    <description>Jersey Jaxrs Component Inject</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/PrintingErrorHandler.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/PrintingErrorHandler.java
new file mode 100644
index 0000000..a8769f3
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/PrintingErrorHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Exception mapper printing the {@link Status#NOT_ACCEPTABLE} error.
+ */
+@Provider
+public class PrintingErrorHandler implements ExceptionMapper<Throwable> {
+
+    @Override
+    public Response toResponse(Throwable throwable) {
+        throwable.printStackTrace();
+
+        Writer result = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(result);
+        throwable.printStackTrace(printWriter);
+        return Response.status(Status.NOT_ACCEPTABLE).entity(result.toString())
+                .build();
+    }
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/Resource.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/Resource.java
new file mode 100644
index 0000000..6c2a965
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/Resource.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Providers;
+
+/**
+ * Resource with {@link Context} injection points.
+ */
+@Path("resource")
+public class Resource {
+
+    @Context
+    Application application;
+    @Context
+    UriInfo info;
+    @Context
+    Request request;
+    @Context
+    HttpHeaders headers;
+    @Context
+    SecurityContext security;
+    @Context
+    Providers providers;
+    @Context
+    ResourceContext resources;
+    @Context
+    Configuration configration;
+
+    @POST
+    @Path("echo")
+    public String returnGivenString(String string) {
+        return string;
+    }
+
+    @POST
+    @Path("reader")
+    public String reader(StringBean bean) {
+        return bean.get();
+    }
+
+    @POST
+    @Path("writer")
+    public StringBean writer(String entity) {
+        return new StringBean(entity);
+    }
+
+    @GET
+    @Path("instance")
+    public String instance() {
+        return StringBeanEntityProviderWithInjectables.computeMask(application,
+                info, request, headers, security, providers, resources,
+                configration);
+    }
+
+    @GET
+    @Path("method")
+    public String method(@Context Application application,
+            @Context UriInfo info, @Context Request request,
+            @Context HttpHeaders headers, @Context SecurityContext security,
+            @Context Providers providers, @Context ResourceContext resources) {
+        return StringBeanEntityProviderWithInjectables.computeMask(application,
+                info, request, headers, security, providers, resources,
+                configration);
+    }
+
+    @GET
+    @Path("application")
+    public String application(@Context Application application) {
+        Set<Object> singletons = application.getSingletons();
+        SingletonWithInjectables singleton = (SingletonWithInjectables) singletons
+                .iterator().next();
+        return singleton.getInjectedContextValues();
+    }
+
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/SingletonWithInjectables.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/SingletonWithInjectables.java
new file mode 100644
index 0000000..22ca84a
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/SingletonWithInjectables.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+/**
+ * Holder for application configuration.
+ */
+public class SingletonWithInjectables {
+
+    private TSAppConfig config;
+
+    public SingletonWithInjectables(TSAppConfig config) {
+        this.config = config;
+    }
+
+    public String getInjectedContextValues() {
+        return config.getInjectedContextValues();
+    }
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBean.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBean.java
new file mode 100644
index 0000000..40bd98d
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBean.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+/**
+ * This is the object which standard implementation does not have a provider
+ * for, even though its some simple String holder. It can also be used as mutable
+ * string.
+ */
+public class StringBean {
+    private String header;
+
+    public StringBean(String header) {
+        this.header = header;
+    }
+
+    public String get() {
+        return header;
+    }
+
+    public void set(String header) {
+        this.header = header;
+    }
+
+    @Override
+    public String toString() {
+        return "StringBean. To get a value, use rather #get() method.";
+    }
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBeanEntityProvider.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBeanEntityProvider.java
new file mode 100644
index 0000000..b8520ab
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBeanEntityProvider.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+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.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Provider for JAX-RS types {@link MessageBodyReader} and {@link MessageBodyWriter}.
+ */
+@Provider
+public class StringBeanEntityProvider implements MessageBodyReader<StringBean>, MessageBodyWriter<StringBean> {
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return StringBean.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public long getSize(StringBean t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return t.get().length();
+    }
+
+    @Override
+    public void writeTo(StringBean t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream) throws IOException,
+            WebApplicationException {
+        entityStream.write(t.get().getBytes());
+    }
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return isWriteable(type, genericType, annotations, mediaType);
+    }
+
+    @Override
+    public StringBean readFrom(Class<StringBean> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+            throws IOException, WebApplicationException {
+        return null;
+    }
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBeanEntityProviderWithInjectables.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBeanEntityProviderWithInjectables.java
new file mode 100644
index 0000000..9b8506f
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/StringBeanEntityProviderWithInjectables.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+/**
+ * Provider with {@link Context} injection points.
+ */
+@Provider
+public class StringBeanEntityProviderWithInjectables extends StringBeanEntityProvider {
+
+    @Context
+    Application application;
+    @Context
+    UriInfo info;
+    @Context
+    Request request;
+    @Context
+    HttpHeaders headers;
+    @Context
+    SecurityContext security;
+    @Context
+    Providers providers;
+    @Context
+    ResourceContext resources;
+    @Context
+    Configuration configuration;
+
+    /**
+     * Chosen decimal as a representation to be more human readable
+     */
+    public static String computeMask(Application application, UriInfo info,
+            Request request, HttpHeaders headers, SecurityContext security,
+            Providers providers, ResourceContext resources,
+            Configuration configuration) {
+        int mask = 1;
+        mask = 10 * mask + (application == null ? 0 : 1);
+        mask = 10 * mask + (info == null ? 0 : 1);
+        mask = 10 * mask + (request == null ? 0 : 1);
+        mask = 10 * mask + (headers == null ? 0 : 1);
+        mask = 10 * mask + (security == null ? 0 : 1);
+        mask = 10 * mask + (providers == null ? 0 : 1);
+        mask = 10 * mask + (resources == null ? 0 : 1);
+        mask = 10 * mask + (configuration == null ? 0 : 1);
+        return String.valueOf(mask);
+    }
+
+    /**
+     * Here, the bitwise operation with mask variable would be more efficient,
+     * but less human readable when sending over the link as a binary number.
+     * Hence, sMask is supposed to be decimal number created by writeTo method.
+     * <p>
+     * If something has not been injected, and thus not written by writeTo, this
+     * static method parses what it was not injected.
+     */
+    public static String notInjected(String sMask) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 1; i != sMask.length(); i++) {
+            sb.append(notInjected(sMask, i));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Here, the bitwise operation with mask variable would be more efficient,
+     * but less human readable when sending over the link as a binary number.
+     * Hence, sMask is supposed to be decimal number created by writeTo method.
+     * <p>
+     * If something has not been injected, and thus not written by writeTo, this
+     * static method parses what it was not injected.
+     */
+    public static final String notInjected(String sMask, int index) {
+        String[] labels = {
+                "Application,", "UriInfo,", "Request,",
+                "HttpHeaders,", "SecurityContext,", "Providers,",
+                "ResourceContext", "Configuration"
+        };
+        String label = "";
+        if (sMask.charAt(index) == '0') {
+            label = labels[index - 1];
+        }
+        return label;
+    }
+
+    @Override
+    public long getSize(StringBean t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType) {
+        return 9;
+    }
+
+    @Override
+    public void writeTo(StringBean t, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream) throws IOException,
+            WebApplicationException {
+        entityStream.write(computeMask(application, info, request, headers,
+                security, providers, resources, configuration).getBytes());
+    }
+
+    @Override
+    public StringBean readFrom(Class<StringBean> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+            throws IOException, WebApplicationException {
+        return new StringBean(computeMask(application, info, request, headers,
+                security, providers, resources, configuration));
+    }
+
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/TSAppConfig.java b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/TSAppConfig.java
new file mode 100644
index 0000000..04101c2
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/java/org/glassfish/jersey/tests/jaxrs/inject/TSAppConfig.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Providers;
+
+/**
+ * Application Configuration with {@link Context} injection points.
+ */
+public class TSAppConfig extends Application {
+
+    @Context
+    UriInfo info;
+    @Context
+    Request request;
+    @Context
+    HttpHeaders headers;
+    @Context
+    SecurityContext security;
+    @Context
+    Providers providers;
+    @Context
+    ResourceContext resources;
+
+    public java.util.Set<java.lang.Class<?>> getClasses() {
+        Set<Class<?>> resources = new HashSet<>();
+        resources.add(Resource.class);
+        resources.add(StringBeanEntityProviderWithInjectables.class);
+        resources.add(PrintingErrorHandler.class);
+        return resources;
+    }
+
+    @Override
+    public Set<Object> getSingletons() {
+        Object single = new SingletonWithInjectables(this);
+        return Collections.singleton(single);
+    }
+
+    public String getInjectedContextValues() {
+        return StringBeanEntityProviderWithInjectables.computeMask(
+                /*
+                 * Spec: 9.2.1 Application Note that this cannot be injected into the Application subclass itself since this would
+                 * create a circular dependency.
+                 * */
+                this, info, request, headers, security, providers, resources,
+                // Configuration injection N/A on Application
+                ClientBuilder.newClient().getConfiguration());
+    }
+}
diff --git a/tests/integration/jaxrs-component-inject/src/main/webapp/WEB-INF/web.xml b/tests/integration/jaxrs-component-inject/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5c07843
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2017, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>CTSJAXRSContextServer</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.jaxrs.inject.TSAppConfig</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>CTSJAXRSContextServer</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+    <session-config>
+        <session-timeout>30</session-timeout>
+    </session-config>
+</web-app>
diff --git a/tests/integration/jaxrs-component-inject/src/test/java/org/glassfish/jersey/tests/jaxrs/inject/ApplicationInjectITCase.java b/tests/integration/jaxrs-component-inject/src/test/java/org/glassfish/jersey/tests/jaxrs/inject/ApplicationInjectITCase.java
new file mode 100644
index 0000000..50c1ff8
--- /dev/null
+++ b/tests/integration/jaxrs-component-inject/src/test/java/org/glassfish/jersey/tests/jaxrs/inject/ApplicationInjectITCase.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017, 2018 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.jaxrs.inject;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link javax.ws.rs.core.Context} injection into JAX-RS components Provider, Resource, Application.
+ *
+ * @author Petr Bouda
+ */
+public class ApplicationInjectITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInstance() throws Exception {
+        WebTarget t = target();
+        t.register(LoggingFeature.class);
+        Response r = t.path("/resource/instance").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("111111111", r.readEntity(String.class));
+    }
+
+    @Test
+    public void testMethod() throws Exception {
+        WebTarget t = target();
+        t.register(LoggingFeature.class);
+        Response r = t.path("/resource/method").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("111111111", r.readEntity(String.class));
+    }
+    @Test
+    public void testApplication() throws Exception {
+        WebTarget t = target();
+        t.register(LoggingFeature.class);
+        Response r = t.path("/resource/application").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("111111111", r.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/jersey-1107/pom.xml b/tests/integration/jersey-1107/pom.xml
new file mode 100644
index 0000000..d2ea930
--- /dev/null
+++ b/tests/integration/jersey-1107/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-1107</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1107</name>
+
+    <description>Servlet integration test - JERSEY-1107 - Thread gets stucked if no MessageBodyWriter is found in ApplicationHandler#writeResponse</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-1107/src/main/java/org/glassfish/jersey/tests/integration/jersey1107/Jersey1107.java b/tests/integration/jersey-1107/src/main/java/org/glassfish/jersey/tests/integration/jersey1107/Jersey1107.java
new file mode 100644
index 0000000..7073d75
--- /dev/null
+++ b/tests/integration/jersey-1107/src/main/java/org/glassfish/jersey/tests/integration/jersey1107/Jersey1107.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey1107;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Jersey1107 extends Application {
+
+    /**
+     * This MessageBodyWriter does not support the "exception/nullpointerexception" media type required by the
+     * {@code Resource#getNpe()} method which should result in an empty {@code MessageBodyWriter} and therefore
+     * an NPE in {@code ApplicationHandler}.
+     *
+     * @see org.glassfish.jersey.tests.integration.jersey1107.Jersey1107.Resource#getNpe()
+     */
+    @Provider
+    @Produces({"exception/ioexception", "exception/webapplicationexception"})
+    public static class ExceptionThrower implements MessageBodyWriter<Exception> {
+
+        @Override
+        public boolean isWriteable(
+                Class<?> type,
+                Type genericType,
+                Annotation[] annotations,
+                MediaType mediaType) {
+            return IOException.class.isAssignableFrom(type) || RuntimeException.class.isAssignableFrom(type);
+        }
+
+        @Override
+        public long getSize(
+                Exception t,
+                Class<?> type,
+                Type genericType,
+                Annotation[] annotations,
+                MediaType mediaType) {
+            return -1;
+        }
+
+        @Override
+        public void writeTo(
+                Exception e,
+                Class<?> type,
+                Type genericType,
+                Annotation[] annotations,
+                MediaType mediaType,
+                MultivaluedMap<String, Object> httpHeaders,
+                OutputStream entityStream) throws IOException, WebApplicationException {
+            // Cannot write anything into the entityStream to ensure the ContainerResponseWriter#writeResponseStatusAndHeaders
+            // in ApplicationHandler#writeResponse is not invoked.
+
+            // Simply throw the given exception.
+            if (e instanceof IOException) {
+                throw (IOException) e;
+            } else {
+                throw (RuntimeException) e;
+            }
+        }
+
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        @Path("/ioe")
+        @Produces("exception/ioexception")
+        public IOException getIoe() {
+            return new IOException();
+        }
+
+        @GET
+        @Path("/wae")
+        @Produces("exception/webapplicationexception")
+        public WebApplicationException getWae() {
+            return new WebApplicationException();
+        }
+
+        @GET
+        @Path("/npe")
+        @Produces("exception/nullpointerexception")
+        public NullPointerException getNpe() {
+            return new NullPointerException("This message should never get to the client!");
+        }
+
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {{
+            add(Resource.class);
+            add(ExceptionThrower.class);
+        }};
+    }
+
+}
diff --git a/tests/integration/jersey-1107/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1107/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5c144f8
--- /dev/null
+++ b/tests/integration/jersey-1107/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<web-app version="2.5"
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1107.Jersey1107</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/tests/integration/jersey-1107/src/test/java/org/glassfish/jersey/tests/integration/jersey1107/ApplicationHandlerITCase.java b/tests/integration/jersey-1107/src/test/java/org/glassfish/jersey/tests/integration/jersey1107/ApplicationHandlerITCase.java
new file mode 100644
index 0000000..11cb216
--- /dev/null
+++ b/tests/integration/jersey-1107/src/test/java/org/glassfish/jersey/tests/integration/jersey1107/ApplicationHandlerITCase.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey1107;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for JERSEY-1107: Thread gets stuck if no MessageBodyWriter is found in ApplicationHandler#writeResponse.
+ * <p/>
+ * If an exception (e.g. NPE caused by non-existent MessageBodyWriter) is thrown in ApplicationHandler#writeResponse before
+ * headers and response status are written by ContainerResponseWriter#writeResponseStatusAndHeaders then the
+ * ContainerResponseWriter#commit in the finally clause will stuck the thread.
+ * <p/>
+ * The purpose of the tests below is to show that a response is returned from the server and none of the threads gets stuck.
+ *
+ * @author Michal Gajdos
+ */
+public class ApplicationHandlerITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig().registerInstances(new Jersey1107());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Checks if a thread gets stuck when an {@code IOException} is thrown from the {@code
+     * MessageBodyWriter#writeTo}.
+     */
+    @Test
+    public void testIOExceptionInWriteResponseMethod() throws Exception {
+        _testExceptionInWriteResponseMethod("ioe", "exception/ioexception", Response.Status.INTERNAL_SERVER_ERROR);
+    }
+
+    /**
+     * Checks if a thread gets stuck when an {@code WebApplicationException} is thrown from the {@code
+     * MessageBodyWriter#writeTo}.
+     */
+    @Test
+    public void testWebApplicationExceptionInWriteResponseMethod() throws Exception {
+        _testExceptionInWriteResponseMethod("wae", "exception/webapplicationexception", Response.Status.INTERNAL_SERVER_ERROR);
+    }
+
+    /**
+     * Checks if a thread gets stuck when no {@code MessageBodyWriter} is found and therefore an {@code NPE} is thrown
+     * when trying to invoke {@code MessageBodyWriter#writeTo} on an empty object.
+     */
+    @Test
+    public void testNullPointerExceptionInWriteResponseMethod() throws Exception {
+        _testExceptionInWriteResponseMethod("npe", "exception/nullpointerexception", Response.Status.INTERNAL_SERVER_ERROR);
+    }
+
+    /**
+     * Creates a request to the server (with the whole process time set to the maximum of 5 seconds) for the given {@code path}
+     * and {@code mediaType} that should result in the {@code expectedResponse}.
+     */
+    private void _testExceptionInWriteResponseMethod(final String path, final String mediaType,
+                                                     final Response.Status expectedResponse) throws Exception {
+        // Executor.
+        final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+        final Future<Response> responseFuture = executor.submit(new Callable<Response>() {
+
+            @Override
+            public Response call() throws Exception {
+                return target().path(path).request(mediaType).get();
+            }
+
+        });
+
+        executor.shutdown();
+        final boolean inTime = executor.awaitTermination(5000, TimeUnit.MILLISECONDS);
+
+        // Asserts.
+        assertTrue(inTime);
+
+        // Response.
+        final Response response = responseFuture.get();
+        assertEquals(expectedResponse.getStatusCode(), response.getStatusInfo().getStatusCode());
+    }
+
+}
diff --git a/tests/integration/jersey-1223/pom.xml b/tests/integration/jersey-1223/pom.xml
new file mode 100644
index 0000000..4bc9d10
--- /dev/null
+++ b/tests/integration/jersey-1223/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>jersey-1223</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1223</name>
+    <url>http://maven.apache.org</url>
+
+    <description>
+        Servlet integration test - JERSEY-1223 - "500 Internal Server Error" for POST request with garbage instead of
+        "Content-Type"
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jersey-1223/src/main/java/org/glassfish/jersey/tests/integration/jersey1223/Jersey1223.java b/tests/integration/jersey-1223/src/main/java/org/glassfish/jersey/tests/integration/jersey1223/Jersey1223.java
new file mode 100644
index 0000000..60fcd3c
--- /dev/null
+++ b/tests/integration/jersey-1223/src/main/java/org/glassfish/jersey/tests/integration/jersey1223/Jersey1223.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1223;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Jersey1223 extends Application {
+
+    @Path(value = "/ContentType")
+    public static class ContentTypeResource {
+
+        @POST
+        @Produces(value = "text/plain")
+        @SuppressWarnings({"UnusedParameters", "JavaDoc"})
+        public void postTest(final String str) {
+            // Ignore to generate response 204 - NoContent.
+        }
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.<Class<?>>singleton(ContentTypeResource.class);
+    }
+}
diff --git a/tests/integration/jersey-1223/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1223/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1634824
--- /dev/null
+++ b/tests/integration/jersey-1223/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<!DOCTYPE web-app PUBLIC
+ "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd" >
+
+<web-app version="2.5"
+         xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1223.Jersey1223</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
+
+
diff --git a/tests/integration/jersey-1223/src/test/java/org/glassfish/jersey/tests/integration/jersey1223/ApplicationHandlerITCase.java b/tests/integration/jersey-1223/src/test/java/org/glassfish/jersey/tests/integration/jersey1223/ApplicationHandlerITCase.java
new file mode 100644
index 0000000..6ea3471
--- /dev/null
+++ b/tests/integration/jersey-1223/src/test/java/org/glassfish/jersey/tests/integration/jersey1223/ApplicationHandlerITCase.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1223;
+
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests JERSEY issue 1223.
+ *
+ * @author Michal Gajdos
+ */
+public class ApplicationHandlerITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(Jersey1223.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInvalidContentTypeHeader() throws Exception {
+        final URL url = new URL(getBaseUri().toString() + "ContentType");
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        connection.setRequestMethod("POST");
+        connection.setRequestProperty("Accept", "text/plain");
+        connection.setRequestProperty("Content-Type", "application/json, app/json");
+
+        connection.setDoOutput(true);
+        connection.connect();
+
+        final OutputStream outputStream = connection.getOutputStream();
+        outputStream.write("HelloWorld!".getBytes());
+        outputStream.write('\n');
+        outputStream.flush();
+
+        assertEquals(400, connection.getResponseCode());
+    }
+}
diff --git a/tests/integration/jersey-1604/pom.xml b/tests/integration/jersey-1604/pom.xml
new file mode 100644
index 0000000..c7ec7b2
--- /dev/null
+++ b/tests/integration/jersey-1604/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>jersey-1604</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1604</name>
+    <url>http://maven.apache.org</url>
+
+    <description>Servlet integration test - JERSEY-1604 - IOException "connection closed" happens on calling delete without parameters</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jersey-1604/src/main/java/org/glassfish/jersey/tests/integration/jersey1604/Jersey1604.java b/tests/integration/jersey-1604/src/main/java/org/glassfish/jersey/tests/integration/jersey1604/Jersey1604.java
new file mode 100644
index 0000000..d4a2bbd
--- /dev/null
+++ b/tests/integration/jersey-1604/src/main/java/org/glassfish/jersey/tests/integration/jersey1604/Jersey1604.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1604;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Application class with test resource that sets {@code Connection} header to {@code close}.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey1604 extends Application {
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.<Class<?>>singleton(TestResource.class);
+    }
+
+    @Path("/")
+    public static class TestResource {
+
+        @DELETE
+        public Response delete() {
+            return Response
+                    .ok()
+                    .type(MediaType.TEXT_PLAIN_TYPE)
+                    .header("Connection", "close")
+                    .build();
+        }
+    }
+}
diff --git a/tests/integration/jersey-1604/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1604/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..91aaa79
--- /dev/null
+++ b/tests/integration/jersey-1604/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<!DOCTYPE web-app PUBLIC
+ "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd" >
+
+<web-app version="2.5"
+         xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1604.Jersey1604</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
+
+
diff --git a/tests/integration/jersey-1604/src/test/java/org/glassfish/jersey/tests/integration/jersey1604/ConnectionCloseITCase.java b/tests/integration/jersey-1604/src/test/java/org/glassfish/jersey/tests/integration/jersey1604/ConnectionCloseITCase.java
new file mode 100644
index 0000000..067cbff
--- /dev/null
+++ b/tests/integration/jersey-1604/src/test/java/org/glassfish/jersey/tests/integration/jersey1604/ConnectionCloseITCase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1604;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests JERSEY issue 1604.
+ *
+ * @author Michal Gajdos
+ */
+public class ConnectionCloseITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(Jersey1604.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new ApacheConnectorProvider());
+    }
+
+    @Test
+    public void testConnectionClose() throws Exception {
+        final Response response = target().request().header("Connection", "close").delete();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.hasEntity(), is(false));
+    }
+}
diff --git a/tests/integration/jersey-1667/pom.xml b/tests/integration/jersey-1667/pom.xml
new file mode 100644
index 0000000..1e0243f
--- /dev/null
+++ b/tests/integration/jersey-1667/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-1667</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1667</name>
+
+    <description>Servlet integration test - JERSEY-1667 - Incorrect handling of file upload errors when temp files cannot be written</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-1667/src/main/java/org/glassfish/jersey/tests/integration/jersey1667/Issue1667Resource.java b/tests/integration/jersey-1667/src/main/java/org/glassfish/jersey/tests/integration/jersey1667/Issue1667Resource.java
new file mode 100644
index 0000000..46ba64c
--- /dev/null
+++ b/tests/integration/jersey-1667/src/main/java/org/glassfish/jersey/tests/integration/jersey1667/Issue1667Resource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1667;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+
+/**
+ * Test resource.
+ *
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class Issue1667Resource {
+
+    @POST
+    @Path("part-file-name")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public String post(
+            @FormDataParam("part") String s,
+            @FormDataParam("part") FormDataContentDisposition d) {
+        return s + ":" + d.getFileName();
+    }
+}
diff --git a/tests/integration/jersey-1667/src/main/java/org/glassfish/jersey/tests/integration/jersey1667/Jersey1667.java b/tests/integration/jersey-1667/src/main/java/org/glassfish/jersey/tests/integration/jersey1667/Jersey1667.java
new file mode 100644
index 0000000..026623c
--- /dev/null
+++ b/tests/integration/jersey-1667/src/main/java/org/glassfish/jersey/tests/integration/jersey1667/Jersey1667.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1667;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.media.multipart.MultiPartProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-1667 reproducer test.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey1667 extends ResourceConfig {
+
+    public Jersey1667() {
+        register(Issue1667Resource.class);
+        register(MultiPartFeature.class);
+
+        register(new MultiPartProperties().bufferThreshold(8192).tempDir("/non-existent-directory").resolver());
+    }
+}
diff --git a/tests/integration/jersey-1667/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1667/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5897705
--- /dev/null
+++ b/tests/integration/jersey-1667/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey1667Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1667.Jersey1667</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey1667Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-1667/src/test/java/org/glassfish/jersey/tests/integration/jersey1667/Jersey1667ITCase.java b/tests/integration/jersey-1667/src/test/java/org/glassfish/jersey/tests/integration/jersey1667/Jersey1667ITCase.java
new file mode 100644
index 0000000..4e9523e
--- /dev/null
+++ b/tests/integration/jersey-1667/src/test/java/org/glassfish/jersey/tests/integration/jersey1667/Jersey1667ITCase.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1667;
+
+import java.util.Arrays;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2160.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey1667ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(MultiPartFeature.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Reproducer method for JERSEY-1667.
+     */
+    @Test
+    public void testJersey1667Fix() {
+        final WebTarget target = target().path("part-file-name");
+
+        char[] chars = new char[10 * 1024];
+        Arrays.fill(chars, 'a');
+        final String body = new String(chars);
+
+        final FormDataMultiPart multiPart = new FormDataMultiPart();
+        final FormDataBodyPart bodyPart = new FormDataBodyPart(FormDataContentDisposition.name("part").fileName("file").build(),
+                body);
+        multiPart.bodyPart(bodyPart);
+
+        final Response response = target.request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE));
+
+        assertThat(response.getStatus(), equalTo(500));
+    }
+}
diff --git a/tests/integration/jersey-1829/pom.xml b/tests/integration/jersey-1829/pom.xml
new file mode 100644
index 0000000..c4f1599
--- /dev/null
+++ b/tests/integration/jersey-1829/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>jersey-1829</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1829</name>
+    <url>http://maven.apache.org</url>
+
+
+    <description>Servlet integration test - JERSEY-1829 - Custom reason phrase in response status does not work</description>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jersey-1829/src/main/java/org/glassfish/jersey/tests/integration/jersey1829/Jersey1829.java b/tests/integration/jersey-1829/src/main/java/org/glassfish/jersey/tests/integration/jersey1829/Jersey1829.java
new file mode 100644
index 0000000..e2398f2
--- /dev/null
+++ b/tests/integration/jersey-1829/src/main/java/org/glassfish/jersey/tests/integration/jersey1829/Jersey1829.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1829;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+/**
+ * Application class with test resource that returns custom status reason phrase.
+ *
+ * @author Miroslav Fuksa
+ */
+public class Jersey1829 extends Application {
+
+    public static final String REASON_PHRASE = "my-phrase";
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.<Class<?>>singleton(TestResource.class);
+    }
+
+    @Path("resource")
+    public static class TestResource {
+
+        @GET
+        @Path("428")
+        public Response get() {
+            return Response.status(new Custom428Type()).build();
+        }
+
+        @GET
+        @Path("428-entity")
+        public Response getWithEntity() {
+            return Response.status(new Custom428Type()).entity("entity").build();
+        }
+    }
+
+    public static class Custom428Type implements Response.StatusType {
+
+        @Override
+        public int getStatusCode() {
+            return 428;
+        }
+
+        @Override
+        public String getReasonPhrase() {
+            return REASON_PHRASE;
+        }
+
+        @Override
+        public Response.Status.Family getFamily() {
+            return Response.Status.Family.CLIENT_ERROR;
+        }
+    }
+
+}
diff --git a/tests/integration/jersey-1829/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1829/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5e51210
--- /dev/null
+++ b/tests/integration/jersey-1829/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<!DOCTYPE web-app PUBLIC
+ "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd" >
+
+<web-app version="2.5"
+         xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1829.Jersey1829</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
+
+
diff --git a/tests/integration/jersey-1829/src/test/java/org/glassfish/jersey/tests/integration/jersey1829/ApplicationHandlerITCase.java b/tests/integration/jersey-1829/src/test/java/org/glassfish/jersey/tests/integration/jersey1829/ApplicationHandlerITCase.java
new file mode 100644
index 0000000..11f81af
--- /dev/null
+++ b/tests/integration/jersey-1829/src/test/java/org/glassfish/jersey/tests/integration/jersey1829/ApplicationHandlerITCase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1829;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests JERSEY issue 1744. Custom status reason phrase returned from the resource method was not propagated out of the
+ * servlet container.
+ *
+ * @author Miroslav Fuksa
+ */
+public class ApplicationHandlerITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(Jersey1829.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testCustomResponse428() {
+        final Response response = target().path("resource/428").request().get();
+        Assert.assertEquals(428, response.getStatusInfo().getStatusCode());
+        Assert.assertEquals("my-phrase", response.getStatusInfo().getReasonPhrase());
+    }
+
+    @Test
+    public void testCustomResponse428WithEntity() {
+        final Response response = target().path("resource/428-entity").request().get();
+        Assert.assertEquals(428, response.getStatusInfo().getStatusCode());
+        Assert.assertEquals("my-phrase", response.getStatusInfo().getReasonPhrase());
+
+    }
+}
diff --git a/tests/integration/jersey-1883/pom.xml b/tests/integration/jersey-1883/pom.xml
new file mode 100644
index 0000000..c9c8fc0
--- /dev/null
+++ b/tests/integration/jersey-1883/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-1883</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1883</name>
+
+    <description>Servlet integration test - JERSEY-1883 - @PostConstruct method called twice on @Singleton</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/Life.java b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/Life.java
new file mode 100644
index 0000000..37effa2
--- /dev/null
+++ b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/Life.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey1883;
+
+import javax.annotation.PostConstruct;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application mixed with resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/rest3")
+@Path("/life")
+public class Life extends Application {
+
+    private static int postConstructCount = 0;
+
+    @PostConstruct
+    public void post_construct() {
+        postConstructCount++;
+    }
+
+    @GET
+    public String greet() {
+        return "hi #" + postConstructCount;
+    }
+}
diff --git a/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/MyApplication.java b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/MyApplication.java
new file mode 100644
index 0000000..764a7b6
--- /dev/null
+++ b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/MyApplication.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey1883;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Explicitly register {@code @Singleton} resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/rest1")
+public class MyApplication extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.singleton(NoLife.class);
+    }
+}
diff --git a/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/NoLife.java b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/NoLife.java
new file mode 100644
index 0000000..9a82223
--- /dev/null
+++ b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/NoLife.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey1883;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Singleton;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+/**
+ * Explicitly registered singleton resource by {@link MyApplication}.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Singleton
+@Path("/no-life")
+public class NoLife extends Application {
+
+    private static int postConstructCount = 0;
+
+    @PostConstruct
+    public void post_construct() {
+        postConstructCount++;
+    }
+
+    @GET
+    public String greet() {
+        return "ciao #" + postConstructCount;
+    }
+}
diff --git a/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/SingletonLife.java b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/SingletonLife.java
new file mode 100644
index 0000000..26ae80b
--- /dev/null
+++ b/tests/integration/jersey-1883/src/main/java/org/glassfish/jersey/tests/integration/jersey1883/SingletonLife.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey1883;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Singleton;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application mixed with resource - singleton class.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Singleton
+@ApplicationPath("/rest2")
+@Path("/singleton-life")
+public class SingletonLife extends Application {
+
+    private static int postConstructCount = 0;
+
+    @PostConstruct
+    public void post_construct() {
+        postConstructCount++;
+    }
+
+    @GET
+    public String greet() {
+        return "hello #" + postConstructCount;
+    }
+}
diff --git a/tests/integration/jersey-1883/src/test/java/org/glassfish/jersey/tests/integration/jersey1883/Jersey1883ITCase.java b/tests/integration/jersey-1883/src/test/java/org/glassfish/jersey/tests/integration/jersey1883/Jersey1883ITCase.java
new file mode 100644
index 0000000..b2fb307
--- /dev/null
+++ b/tests/integration/jersey-1883/src/test/java/org/glassfish/jersey/tests/integration/jersey1883/Jersey1883ITCase.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey1883;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Jersey1883ITCase extends JerseyTest {
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testGetRestNoLife() throws Exception {
+        Response response = target("rest1").path("no-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("ciao #1"));
+
+        response = target("rest1").path("no-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("ciao #1"));
+
+        response = target("rest1").path("no-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("ciao #1"));
+
+        response = target("rest1").path("no-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("ciao #1"));
+    }
+
+    @Test
+    public void testGetRestSingletonLife() throws Exception {
+        Response response = target("rest2").path("singleton-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hello #1"));
+
+        response = target("rest2").path("singleton-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hello #1"));
+
+        response = target("rest2").path("singleton-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hello #1"));
+
+        response = target("rest2").path("singleton-life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hello #1"));
+    }
+
+    @Test
+    public void testGetRestLife() throws Exception {
+        Response response = target("rest3").path("life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hi #2"));
+
+        response = target("rest3").path("life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hi #3"));
+
+        response = target("rest3").path("life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hi #4"));
+
+        response = target("rest3").path("life").request().get();
+        assertThat(response.readEntity(String.class), equalTo("hi #5"));
+    }
+
+}
diff --git a/tests/integration/jersey-1928/pom.xml b/tests/integration/jersey-1928/pom.xml
new file mode 100644
index 0000000..5bbc778
--- /dev/null
+++ b/tests/integration/jersey-1928/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>jersey-1928</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1928</name>
+    <url>http://maven.apache.org</url>
+
+
+    <description>Servlet integration test - JERSEY-1928 - Odd behaviour with empty "path info"</description>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jersey-1928/src/main/java/org/glassfish/jersey/tests/integration/jersey1928/Jersey1928.java b/tests/integration/jersey-1928/src/main/java/org/glassfish/jersey/tests/integration/jersey1928/Jersey1928.java
new file mode 100644
index 0000000..dde2159
--- /dev/null
+++ b/tests/integration/jersey-1928/src/main/java/org/glassfish/jersey/tests/integration/jersey1928/Jersey1928.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1928;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/jersey1928")
+public class Jersey1928 extends Application {
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        public String list() {
+            return "list";
+        }
+
+        @GET
+        @Path("{id}")
+        public String get(@PathParam("id") final String id) {
+            return id;
+        }
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.<Class<?>>singleton(Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-1928/src/test/java/org/glassfish/jersey/tests/integration/jersey1928/Jersey1928ITCase.java b/tests/integration/jersey-1928/src/test/java/org/glassfish/jersey/tests/integration/jersey1928/Jersey1928ITCase.java
new file mode 100644
index 0000000..8fa797b
--- /dev/null
+++ b/tests/integration/jersey-1928/src/test/java/org/glassfish/jersey/tests/integration/jersey1928/Jersey1928ITCase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1928;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests JERSEY issue 1928.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey1928ITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testJersey1928List() throws Exception {
+        final Response response = target("jersey1928").request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("list"));
+    }
+
+    @Test
+    public void testJersey1928Id() throws Exception {
+        final Response response = target("jersey1928").path("id").request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("id"));
+    }
+}
diff --git a/tests/integration/jersey-1960/pom.xml b/tests/integration/jersey-1960/pom.xml
new file mode 100644
index 0000000..4de96cb
--- /dev/null
+++ b/tests/integration/jersey-1960/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-1960</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1960</name>
+
+    <description>Servlet integration test - JERSEY-1960 - Servlet artifact injection into singleton providers.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/EchoResource.java b/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/EchoResource.java
new file mode 100644
index 0000000..ca3b1a9
--- /dev/null
+++ b/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/EchoResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1960;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("echo")
+public class EchoResource {
+
+    @POST
+    @Consumes("text/plain")
+    @Produces("text/plain")
+    public String echo(String message) {
+        return message + "." + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/Jersey1960App.java b/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/Jersey1960App.java
new file mode 100644
index 0000000..8a87f0b
--- /dev/null
+++ b/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/Jersey1960App.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1960;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application for the JERSEY-1960 reproducer test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("application_path")
+public class Jersey1960App extends Application {
+    @SuppressWarnings({"unchecked"})
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Stream.of(EchoResource.class, RequestFilter.class).collect(Collectors.toSet());
+    }
+}
diff --git a/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/RequestFilter.java b/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/RequestFilter.java
new file mode 100644
index 0000000..fde6d21
--- /dev/null
+++ b/tests/integration/jersey-1960/src/main/java/org/glassfish/jersey/tests/integration/jersey1960/RequestFilter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1960;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * Filter testing injection support for of servlet artifacts.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@PreMatching
+public class RequestFilter implements ContainerRequestFilter {
+    public static final String REQUEST_NUMBER = "request-number";
+    @Context
+    private HttpServletRequest hsReq;
+    @Context
+    private HttpServletResponse hsResp;
+    @Context
+    private ServletContext sCtx;
+    @Context
+    private ServletConfig sCfg;
+
+    @Override
+    public void filter(final ContainerRequestContext ctx) throws IOException {
+        final StringBuilder sb = new StringBuilder();
+
+        // First, make sure there are no null injections.
+        if (hsReq == null) {
+            sb.append("HttpServletRequest is null.\n");
+        }
+        if (hsResp == null) {
+            sb.append("HttpServletResponse is null.\n");
+        }
+        if (sCtx == null) {
+            sb.append("ServletContext is null.\n");
+        }
+        if (sCfg == null) {
+            sb.append("ServletConfig is null.\n");
+        }
+
+        if (sb.length() > 0) {
+            ctx.abortWith(Response.serverError().entity(sb.toString()).build());
+        }
+
+        // let's also test some method calls
+        int flags = 0;
+
+        if ("/jersey-1960".equals(hsReq.getServletPath())) {
+            flags += 1;
+        }
+        if (!hsResp.isCommitted()) {
+            flags += 10;
+        }
+        if (!sCtx.getServerInfo().isEmpty()) {
+            flags += 100;
+        }
+        if (sCfg.getServletContext() == sCtx) {
+            flags += 1000;
+        }
+        final String header = hsReq.getHeader(REQUEST_NUMBER);
+
+        ctx.setEntityStream(new ByteArrayInputStream(("filtered-" + flags + "-" + header).getBytes(
+                MessageUtils.getCharset(ctx.getMediaType()))));
+    }
+}
diff --git a/tests/integration/jersey-1960/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1960/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..8ba90c2
--- /dev/null
+++ b/tests/integration/jersey-1960/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey1960Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1960.Jersey1960App</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey1960Servlet</servlet-name>
+        <url-pattern>/jersey-1960/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-1960/src/test/java/org/glassfish/jersey/tests/integration/jersey1960/Jersey1960ITCase.java b/tests/integration/jersey-1960/src/test/java/org/glassfish/jersey/tests/integration/jersey1960/Jersey1960ITCase.java
new file mode 100644
index 0000000..da03ead
--- /dev/null
+++ b/tests/integration/jersey-1960/src/test/java/org/glassfish/jersey/tests/integration/jersey1960/Jersey1960ITCase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1960;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer tests for JERSEY-1960.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class Jersey1960ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey1960App();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Reproducer method for JERSEY-1960.
+     */
+    @Test
+    public void testJersey1960Fix() {
+        for (int i = 0; i < 10; i++) {
+            String response = target().path("jersey-1960/echo").request().header(RequestFilter.REQUEST_NUMBER, i)
+                    .post(Entity.text("test"), String.class);
+            // Assert that the request has been filtered and processed by the echo method.
+            assertEquals(new EchoResource().echo("filtered-1111-" + i), response);
+        }
+    }
+}
diff --git a/tests/integration/jersey-1964/pom.xml b/tests/integration/jersey-1964/pom.xml
new file mode 100644
index 0000000..b6615c6
--- /dev/null
+++ b/tests/integration/jersey-1964/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-1964</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-1964</name>
+
+    <description>Servlet integration test - JERSEY-1964 - NPE with Jersey 1 + Jackson 2 JAX-RS JSON provider</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-metainf-services</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-1964/src/main/java/org/glassfish/jersey/tests/integration/jersey1964/Issue1964Resource.java b/tests/integration/jersey-1964/src/main/java/org/glassfish/jersey/tests/integration/jersey1964/Issue1964Resource.java
new file mode 100644
index 0000000..839fc53
--- /dev/null
+++ b/tests/integration/jersey-1964/src/main/java/org/glassfish/jersey/tests/integration/jersey1964/Issue1964Resource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1964;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource.
+ *
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class Issue1964Resource {
+
+    public static class JsonStringWrapper {
+
+        private String value;
+
+        public JsonStringWrapper() {
+        }
+
+        public JsonStringWrapper(final String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(final String value) {
+            this.value = value;
+        }
+    }
+
+    @PUT
+    @Consumes("application/json")
+    @Produces("application/json")
+    public JsonStringWrapper put(final JsonStringWrapper wrapper) {
+        return wrapper;
+    }
+}
diff --git a/tests/integration/jersey-1964/src/main/java/org/glassfish/jersey/tests/integration/jersey1964/Jersey1964.java b/tests/integration/jersey-1964/src/main/java/org/glassfish/jersey/tests/integration/jersey1964/Jersey1964.java
new file mode 100644
index 0000000..3dd1b6b
--- /dev/null
+++ b/tests/integration/jersey-1964/src/main/java/org/glassfish/jersey/tests/integration/jersey1964/Jersey1964.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1964;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-1964 reproducer test.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey1964 extends ResourceConfig {
+
+    public Jersey1964() {
+        register(Issue1964Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-1964/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-1964/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..7736f49
--- /dev/null
+++ b/tests/integration/jersey-1964/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey1964Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey1964.Jersey1964</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey1964Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-1964/src/test/java/org/glassfish/jersey/tests/integration/jersey1964/Jersey1964ITCase.java b/tests/integration/jersey-1964/src/test/java/org/glassfish/jersey/tests/integration/jersey1964/Jersey1964ITCase.java
new file mode 100644
index 0000000..8329fd5
--- /dev/null
+++ b/tests/integration/jersey-1964/src/test/java/org/glassfish/jersey/tests/integration/jersey1964/Jersey1964ITCase.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey1964;
+
+import java.net.ConnectException;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Reproducer tests for JERSEY-1964.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey1964ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey1964();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testJackson2JsonPut() throws Exception {
+        final Response response = target().request().put(Entity.json(new Issue1964Resource.JsonStringWrapper("foo")));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(Issue1964Resource.JsonStringWrapper.class).getValue(), equalTo("foo"));
+    }
+
+    @Test(expected = ConnectException.class)
+    public void testJackson2JsonGetInvalidEndpoint() throws Throwable {
+        try {
+            ClientBuilder.newClient()
+                    .target("http://localhost:1234")
+                    .request()
+                    .get();
+
+            fail("End-point shouldn't exist.");
+        } catch (final ProcessingException pe) {
+            throw pe.getCause();
+        }
+    }
+
+    @Test(expected = ConnectException.class)
+    public void testJackson2JsonPutInvalidEndpoint() throws Throwable {
+        try {
+            ClientBuilder.newClient()
+                    .target("http://localhost:1234")
+                    .request()
+                    .put(Entity.json(new Issue1964Resource.JsonStringWrapper("foo")));
+
+            fail("End-point shouldn't exist.");
+        } catch (final ProcessingException pe) {
+            throw pe.getCause();
+        }
+    }
+}
diff --git a/tests/integration/jersey-2031/pom.xml b/tests/integration/jersey-2031/pom.xml
new file mode 100644
index 0000000..7b0eea3
--- /dev/null
+++ b/tests/integration/jersey-2031/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2031</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2031</name>
+
+    <description>Servlet integration test - JERSEY-2031 - jersey 2.1 explicit viewable resolvingClass missed when do a jsp tag</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-jsp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2031/src/main/java/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource.java b/tests/integration/jersey-2031/src/main/java/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource.java
new file mode 100644
index 0000000..1e9fa59
--- /dev/null
+++ b/tests/integration/jersey-2031/src/main/java/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2031;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+
+/**
+ * Test resource.
+ *
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class Issue2031Resource {
+
+    private static final Model model;
+    private static final String absolutePath;
+
+    static {
+        absolutePath = "/" + Issue2031Resource.class.getName().replaceAll("\\.", "/").replace('$', '/') + "/index.jsp";
+        model = new Model();
+    }
+
+    @GET
+    @Path("viewable-relative")
+    @Produces("text/html")
+    public Viewable viewableRelative() {
+        return new Viewable("index", model);
+    }
+
+    @GET
+    @Path("viewable-absolute")
+    @Produces("text/html")
+    public Viewable viewableAbsolute() {
+        return new Viewable(absolutePath, model);
+    }
+
+    @GET
+    @Path("template-relative")
+    @Produces("text/html")
+    @Template(name = "index")
+    public Model templateRelative() {
+        return model;
+    }
+
+    @GET
+    @Path("template-absolute")
+    @Produces("text/html")
+    @Template(
+            name = "/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/index.jsp")
+    public Model templateAbsolute() {
+        return model;
+    }
+
+    public static class Model {
+
+        private String index = "index";
+        private String include = "include";
+
+        public String getIndex() {
+            return index;
+        }
+
+        public String getInclude() {
+            return include;
+        }
+    }
+}
diff --git a/tests/integration/jersey-2031/src/main/java/org/glassfish/jersey/tests/integration/jersey2031/Jersey2031.java b/tests/integration/jersey-2031/src/main/java/org/glassfish/jersey/tests/integration/jersey2031/Jersey2031.java
new file mode 100644
index 0000000..3c673eb
--- /dev/null
+++ b/tests/integration/jersey-2031/src/main/java/org/glassfish/jersey/tests/integration/jersey2031/Jersey2031.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2031;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
+
+/**
+ * JAX-RS application for the JERSEY-2031 reproducer test.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2031 extends ResourceConfig {
+
+    public Jersey2031() {
+        register(Issue2031Resource.class);
+        register(JspMvcFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2031/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2031/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..18fc4e3
--- /dev/null
+++ b/tests/integration/jersey-2031/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>jersey2031</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2031.Jersey2031</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>jersey2031</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2031/src/main/webapp/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/include.jsp b/tests/integration/jersey-2031/src/main/webapp/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/include.jsp
new file mode 100644
index 0000000..6c914c2
--- /dev/null
+++ b/tests/integration/jersey-2031/src/main/webapp/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/include.jsp
@@ -0,0 +1,22 @@
+<%--
+
+    Copyright (c) 2013, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+${model.include}
diff --git a/tests/integration/jersey-2031/src/main/webapp/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/index.jsp b/tests/integration/jersey-2031/src/main/webapp/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/index.jsp
new file mode 100644
index 0000000..cbebedb
--- /dev/null
+++ b/tests/integration/jersey-2031/src/main/webapp/org/glassfish/jersey/tests/integration/jersey2031/Issue2031Resource/index.jsp
@@ -0,0 +1,39 @@
+<%--
+
+    Copyright (c) 2013, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Book</title>
+    </head>
+    <body>
+
+    ${model.index}
+
+    <rbt:include page="include.jsp"/>
+
+    </body>
+</html>
diff --git a/tests/integration/jersey-2031/src/test/java/org/glassfish/jersey/tests/integration/jersey2031/Jersey2031ITCase.java b/tests/integration/jersey-2031/src/test/java/org/glassfish/jersey/tests/integration/jersey2031/Jersey2031ITCase.java
new file mode 100644
index 0000000..594129b
--- /dev/null
+++ b/tests/integration/jersey-2031/src/test/java/org/glassfish/jersey/tests/integration/jersey2031/Jersey2031ITCase.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2031;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2164.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2031ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2031();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testTemplateRelative() throws Exception {
+        _test("template-relative");
+    }
+
+    @Test
+    public void testTemplateAbsolute() throws Exception {
+        _test("template-absolute");
+    }
+
+    @Test
+    public void testViewableRelative() throws Exception {
+        _test("viewable-relative");
+    }
+
+    @Test
+    public void testViewableAbsolute() throws Exception {
+        _test("viewable-absolute");
+    }
+
+    private void _test(final String path) throws Exception {
+        final Response response = target(path).request("text/html").get();
+        final String page = response.readEntity(String.class);
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(page, containsString("index"));
+        assertThat(page, containsString("include"));
+    }
+}
diff --git a/tests/integration/jersey-2136/pom.xml b/tests/integration/jersey-2136/pom.xml
new file mode 100644
index 0000000..5e797da
--- /dev/null
+++ b/tests/integration/jersey-2136/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2136</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2136</name>
+
+    <description>Servlet integration test - JERSEY-2136 - Jersey 2 doesn't work with Google Application Engine</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.appengine</groupId>
+            <artifactId>appengine-api-1.0-sdk</artifactId>
+            <version>${gae.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+
+            <!-- TODO: The test was skipped, including the hooked app-engine start && stop. The app-engine process was sometimes
+            left hanging after the end of the build and kept blocking the port. -->
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <!--<plugin>-->
+                <!--<groupId>com.google.appengine</groupId>-->
+                <!--<artifactId>appengine-maven-plugin</artifactId>-->
+                <!--<version>${gae.version}</version>-->
+                <!--<configuration>-->
+
+                    <!--<port>${jersey.config.test.container.port}</port>-->
+                    <!--<offline>true</offline>-->
+                <!--</configuration>-->
+                <!--<executions>-->
+                    <!--<execution>-->
+                        <!--<id>start-gae</id>-->
+                        <!--<phase>pre-integration-test</phase>-->
+                        <!--<goals>-->
+                            <!--<goal>devserver_start</goal>-->
+                        <!--</goals>-->
+                    <!--</execution>-->
+                    <!--<execution>-->
+                        <!--<id>stop-gae</id>-->
+                        <!--<phase>post-integration-test</phase>-->
+                        <!--<goals>-->
+                            <!--<goal>devserver_stop</goal>-->
+                        <!--</goals>-->
+                    <!--</execution>-->
+                <!--</executions>-->
+            <!--</plugin>-->
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2136/src/main/java/org/glassfish/jersey/tests/integration/jersey2136/Issue2136Resource.java b/tests/integration/jersey-2136/src/main/java/org/glassfish/jersey/tests/integration/jersey2136/Issue2136Resource.java
new file mode 100644
index 0000000..ede5954
--- /dev/null
+++ b/tests/integration/jersey-2136/src/main/java/org/glassfish/jersey/tests/integration/jersey2136/Issue2136Resource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2136;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Test resource.
+ *
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class Issue2136Resource {
+
+    @GET
+    public String get() {
+        return "Hello, GAE!";
+    }
+}
diff --git a/tests/integration/jersey-2136/src/main/java/org/glassfish/jersey/tests/integration/jersey2136/Jersey2136.java b/tests/integration/jersey-2136/src/main/java/org/glassfish/jersey/tests/integration/jersey2136/Jersey2136.java
new file mode 100644
index 0000000..b7f604e
--- /dev/null
+++ b/tests/integration/jersey-2136/src/main/java/org/glassfish/jersey/tests/integration/jersey2136/Jersey2136.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2136;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2136 reproducer test.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2136 extends ResourceConfig {
+
+    public Jersey2136() {
+        register(Issue2136Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-2136/src/main/webapp/WEB-INF/appengine-web.xml b/tests/integration/jersey-2136/src/main/webapp/WEB-INF/appengine-web.xml
new file mode 100644
index 0000000..fc291a1
--- /dev/null
+++ b/tests/integration/jersey-2136/src/main/webapp/WEB-INF/appengine-web.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
+    <application>jersey2136</application>
+    <version>1</version>
+    <threadsafe>true</threadsafe>
+</appengine-web-app>
diff --git a/tests/integration/jersey-2136/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2136/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..2c67157
--- /dev/null
+++ b/tests/integration/jersey-2136/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2136Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2136.Jersey2136</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2136Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2136/src/test/java/org/glassfish/jersey/tests/integration/jersey2136/Jersey2136ITCase.java b/tests/integration/jersey-2136/src/test/java/org/glassfish/jersey/tests/integration/jersey2136/Jersey2136ITCase.java
new file mode 100644
index 0000000..418666a
--- /dev/null
+++ b/tests/integration/jersey-2136/src/test/java/org/glassfish/jersey/tests/integration/jersey2136/Jersey2136ITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2136;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2164.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2136ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2136();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    // This test requires app engine to be launched by a maven plugin,
+    // which was causing intermittent problems with hanging app engine process.
+    // After un-ignoring this test, make sure to uncomment the app engine plugin
+    // execution in the project's pom.xml
+    @Ignore
+    @Test
+    public void testGet() throws Exception {
+        final Response response = target().request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("Hello, GAE!"));
+    }
+}
diff --git a/tests/integration/jersey-2137/pom.xml b/tests/integration/jersey-2137/pom.xml
new file mode 100644
index 0000000..d5b48c5
--- /dev/null
+++ b/tests/integration/jersey-2137/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2137</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2137</name>
+
+    <description>Jersey CDI/JTA test web application, JERSEY-2137 reproducer</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.transaction</groupId>
+            <artifactId>javax.transaction-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/Account.java b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/Account.java
new file mode 100644
index 0000000..1c414b7
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/Account.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2137;
+
+import java.io.Serializable;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * Entity bean that maintains information on account balance.
+ * This is to help determine if rollback happens or not, when
+ * entity bean is accessed from a transactional CDI beans.
+ * Entity beans have implicit JTA support, so this will
+ * save us some lines of code.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Entity
+public class Account implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    private Long id;
+
+    /**
+     * Get the account id.
+     *
+     * @return account id.
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Set the account id.
+     *
+     * @param id account id.
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    private long balance;
+
+    /**
+     * Get the value of balance
+     *
+     * @return the value of balance
+     */
+    public long getBalance() {
+        return balance;
+    }
+
+    /**
+     * Set the value of balance
+     *
+     * @param balance new value of balance
+     */
+    public void setBalance(long balance) {
+        this.balance = balance;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int hash = 0;
+        hash += (id != null ? id.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof Account)) {
+            return false;
+        }
+        Account other = (Account) object;
+        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "org.glassfish.jersey.tests.integration.jersey2137.Account[ id=" + id + " ]";
+    }
+}
diff --git a/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/CdiTransactionalNoRollbackResource.java b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/CdiTransactionalNoRollbackResource.java
new file mode 100644
index 0000000..3ce59b1
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/CdiTransactionalNoRollbackResource.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2137;
+
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import javax.enterprise.context.RequestScoped;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.transaction.Transactional;
+
+/**
+ * Request scoped transactional CDI bean registered as JAX-RS resource class.
+ * Part of JERSEY-2137 reproducer. {@link javax.ws.rs.WebApplicationException}
+ * thrown in the resource method below should drive the response as specified
+ * in the JAX-RS spec regardless
+ * on the {@link javax.transaction.Transactional#dontRollbackOn()} value.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Transactional(dontRollbackOn = WebApplicationException.class)
+@Path("cdi-transactional-no-rollback")
+public class CdiTransactionalNoRollbackResource {
+
+    @PersistenceContext(unitName = "Jersey2137PU")
+    private EntityManager entityManager;
+
+    @Path("{a}")
+    @PUT
+    public void putBalance(@PathParam("a") long a, String balance) {
+        final Account account = entityManager.find(Account.class, a);
+        if (account == null) {
+            Account newAccount = new Account();
+            newAccount.setId(a);
+            newAccount.setBalance(Long.decode(balance));
+            entityManager.persist(newAccount);
+            throw new WebApplicationException(Response.ok("New accout created.").build());
+        } else {
+            account.setBalance(Long.decode(balance));
+            entityManager.merge(account);
+            throw new WebApplicationException(Response.ok("Balance updated.").build());
+        }
+    }
+}
diff --git a/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/CdiTransactionalResource.java b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/CdiTransactionalResource.java
new file mode 100644
index 0000000..0132a91
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/CdiTransactionalResource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2137;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import javax.enterprise.context.RequestScoped;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.transaction.Transactional;
+
+/**
+ * Request scoped transactional CDI bean registered as JAX-RS resource class.
+ * Part of JERSEY-2137 reproducer. {@link javax.ws.rs.WebApplicationException}
+ * thrown in the resource method below should drive the response as specified
+ * in the JAX-RS spec.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Transactional
+@Path("cdi-transactional")
+public class CdiTransactionalResource {
+
+    @PersistenceContext(unitName = "Jersey2137PU")
+    private EntityManager entityManager;
+
+    @Path("{a}")
+    @GET
+    public String getBalance(@PathParam("a") long a) {
+        final Account account = entityManager.find(Account.class, a);
+        if (account == null) {
+            throw new WebApplicationException(
+                    Response.status(Response.Status.BAD_REQUEST).entity(String.format("Account %d not found", a)).build());
+        } else {
+            return String.format("%d", account.getBalance());
+        }
+    }
+
+    @Path("{a}")
+    @PUT
+    public void putBalance(@PathParam("a") long a, String balance) {
+        final Account account = entityManager.find(Account.class, a);
+        if (account == null) {
+            Account newAccount = new Account();
+            newAccount.setId(a);
+            newAccount.setBalance(Long.decode(balance));
+            entityManager.persist(newAccount);
+        } else {
+            account.setBalance(Long.decode(balance));
+            entityManager.merge(account);
+        }
+    }
+
+    @POST
+    public String transferMoney(@QueryParam("from") long from, @QueryParam("to") long to, String amount) {
+
+        final Account toAccount = entityManager.find(Account.class, to);
+
+        if (toAccount != null) {
+            try {
+                toAccount.setBalance(toAccount.getBalance() + Long.decode(amount));
+                entityManager.merge(toAccount);
+                final Account fromAccount = entityManager.find(Account.class, from);
+                fromAccount.setBalance(fromAccount.getBalance() - Long.decode(amount));
+                if (fromAccount.getBalance() < 0) {
+                    throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+                            .entity("Transaction failed. Not enough money on the funding account.").build());
+                }
+                entityManager.merge(fromAccount);
+                return "Transaction sucessful.";
+            } catch (Exception e) {
+                if (e instanceof WebApplicationException) {
+                    throw (WebApplicationException) e;
+                } else {
+                    throw new WebApplicationException(
+                            Response.status(Response.Status.BAD_REQUEST).entity("Something bad happened.").build());
+                }
+            }
+        } else {
+            throw new WebApplicationException(
+                    Response.status(Response.Status.BAD_REQUEST).entity("Target account not found.").build());
+        }
+    }
+}
diff --git a/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/TestApplication.java b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/TestApplication.java
new file mode 100644
index 0000000..8e3f6d0
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/java/org/glassfish/jersey/tests/integration/jersey2137/TestApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2137;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources for JERSEY-2137 reproducer.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class TestApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(CdiTransactionalResource.class);
+        classes.add(CdiTransactionalNoRollbackResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/jersey-2137/src/main/webapp/WEB-INF/beans.xml b/tests/integration/jersey-2137/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/jersey-2137/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2137/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3c18370
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+</web-app>
diff --git a/tests/integration/jersey-2137/src/main/webapp/WEB-INF/weblogic.xml b/tests/integration/jersey-2137/src/main/webapp/WEB-INF/weblogic.xml
new file mode 100644
index 0000000..1d2a9ef
--- /dev/null
+++ b/tests/integration/jersey-2137/src/main/webapp/WEB-INF/weblogic.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd">
+
+    <wls:library-ref>
+        <wls:library-name>jax-rs</wls:library-name>
+        <wls:specification-version>2.0</wls:specification-version>
+        <wls:exact-match>true</wls:exact-match>
+    </wls:library-ref>
+</wls:weblogic-web-app>
diff --git a/tests/integration/jersey-2137/src/test/java/org/glassfish/jersey/tests/integration/jersey2137/WaeExceptionMappingTest.java b/tests/integration/jersey-2137/src/test/java/org/glassfish/jersey/tests/integration/jersey2137/WaeExceptionMappingTest.java
new file mode 100644
index 0000000..c722b92
--- /dev/null
+++ b/tests/integration/jersey-2137/src/test/java/org/glassfish/jersey/tests/integration/jersey2137/WaeExceptionMappingTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2137;
+
+import java.net.URI;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+/**
+ * Reproducer for JERSEY-2137.
+ * Ensure that generated {@link WebApplicationException} is propagated
+ * via transactional CDI call and mapped to response according to JAX-RS spec.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class WaeExceptionMappingTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("jersey-2137").build();
+    }
+
+    /**
+     * Test all {@javax.transaction.Transactional}
+     * annotated CDI beans. The test scenario is as follows.
+     * Set two accounts via the CDI bean that avoids rollback.
+     * Should any rollback happen there, we would not be able
+     * to store any data in JPA. Next try to make two transactions,
+     * first of them should be finished without errors,
+     * during the other one, a rollback is expected.
+     * The rollback should avoid partial data to be written
+     * to the first account.
+     */
+    @Test
+    public void testTransactions() {
+
+        final WebTarget cdiResource = target().path("cdi-transactional");
+        final WebTarget cdiResourceNoRollback = target().path("cdi-transactional-no-rollback");
+
+        Response response;
+        String responseBody;
+
+        // account 12 -> insert 1000:
+        response = cdiResourceNoRollback.path("12").request().put(Entity.text("1000"));
+        assertThat(response.getStatus(), equalTo(200));
+
+        // account 13 -> insert 1000:
+        response = cdiResourceNoRollback.path("13").request().put(Entity.text("1000"));
+        assertThat(response.getStatus(), equalTo(200));
+
+        // transfer 1000 from 13 to 12:
+        response = cdiResource.queryParam("from", "13").queryParam("to", "12").request().post(Entity.text("1000"));
+        assertThat(response.getStatus(), equalTo(200));
+
+        // ensure 12 has balance 2000:
+        response = cdiResource.path("12").request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        responseBody = response.readEntity(String.class);
+        assertThat(responseBody, equalTo("2000"));
+
+        // try to transfer 1000 from non-existing 8 to 12, this time the transaction should fail:
+        response = cdiResource.queryParam("from", "8").queryParam("to", "12").request().post(Entity.text("1000"));
+        assertThat(response.getStatus(), equalTo(400));
+
+        // ensure 12 balance has not changed:
+        response = cdiResource.path("12").request().get();
+        assertThat(response.getStatus(), equalTo(200));
+        responseBody = response.readEntity(String.class);
+        assertThat(responseBody, equalTo("2000"));
+    }
+}
diff --git a/tests/integration/jersey-2154/pom.xml b/tests/integration/jersey-2154/pom.xml
new file mode 100644
index 0000000..6dc290f
--- /dev/null
+++ b/tests/integration/jersey-2154/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2154</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2154</name>
+
+    <description>Jersey CDI/EJB test web application, JERSEY-2154 reproducer</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.enterprise</groupId>
+            <artifactId>cdi-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.ejb</groupId>
+            <artifactId>javax.ejb-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/CdiResource.java b/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/CdiResource.java
new file mode 100644
index 0000000..8539fc7
--- /dev/null
+++ b/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/CdiResource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2154;
+
+import javax.ejb.EJB;
+import javax.enterprise.context.RequestScoped;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * Request scoped CDI bean injected with EJB bean.
+ * Part of JERSEY-2154 reproducer. {@link javax.ejb.EJBException}
+ * thrown in the injected EJB bean should get unwrapped
+ * even when no EJB-backed JAX-RS resources have been registered.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RequestScoped
+@Path("cdi")
+public class CdiResource {
+
+    @EJB EjbBean ejbBean;
+
+    @GET
+    public String get() {
+        return ejbBean.exceptionDrivenResponse();
+    }
+}
diff --git a/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/EjbBean.java b/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/EjbBean.java
new file mode 100644
index 0000000..bd19227
--- /dev/null
+++ b/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/EjbBean.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2154;
+
+import javax.ejb.EJBException;
+import javax.ejb.Stateless;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+/**
+ * EJB bean to reproduce JERSEY-2154. Bellow generated {@link WebApplicationException}
+ * should get mapped to response even when wrapped into an {@link EJBException}
+ * by the underlying EJB container.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Stateless
+public class EjbBean {
+
+    /**
+     * This is to make sure the exception thrown gets wrapped with an {@link EJBException}.
+     *
+     * @return nothing, just throws an exception.
+     */
+    public String exceptionDrivenResponse() {
+        throw new WebApplicationException(Response.ok("If you read this, the exception got mapped successfully!").build());
+    }
+}
diff --git a/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/TestApplication.java b/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/TestApplication.java
new file mode 100644
index 0000000..a794af9
--- /dev/null
+++ b/tests/integration/jersey-2154/src/main/java/org/glassfish/jersey/tests/integration/jersey2154/TestApplication.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2154;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources for JERSEY-2154 reproducer.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("/*")
+public class TestApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<Class<?>>();
+        classes.add(CdiResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/jersey-2154/src/main/webapp/WEB-INF/beans.xml b/tests/integration/jersey-2154/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..07df368
--- /dev/null
+++ b/tests/integration/jersey-2154/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<beans/>
diff --git a/tests/integration/jersey-2154/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2154/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3c18370
--- /dev/null
+++ b/tests/integration/jersey-2154/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+</web-app>
diff --git a/tests/integration/jersey-2154/src/test/java/org/glassfish/jersey/tests/integration/jersey2154/EjbExceptionMappingTest.java b/tests/integration/jersey-2154/src/test/java/org/glassfish/jersey/tests/integration/jersey2154/EjbExceptionMappingTest.java
new file mode 100644
index 0000000..564fc14
--- /dev/null
+++ b/tests/integration/jersey-2154/src/test/java/org/glassfish/jersey/tests/integration/jersey2154/EjbExceptionMappingTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2154;
+
+import java.net.URI;
+import javax.ejb.EJBException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.core.StringContains.containsString;
+
+/**
+ * Reproducer for JERSEY-2154.
+ * Test generated {@link WebApplicationException} is propagated
+ * via CDI call and mapped to 200 response, even when wrapped with an {@link EJBException}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class EjbExceptionMappingTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("jersey-2154").build();
+    }
+
+    /**
+     * The only test needed. Should the original {@link WebApplicationException}
+     * not get unwrapped, we would end up with 500 status code.
+     */
+    @Test
+    public void testInjection() {
+
+        final WebTarget cdiResource = target().path("cdi");
+
+        Response response = cdiResource.request().get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), containsString("exception got mapped"));
+    }
+}
diff --git a/tests/integration/jersey-2160/pom.xml b/tests/integration/jersey-2160/pom.xml
new file mode 100644
index 0000000..8e32aac
--- /dev/null
+++ b/tests/integration/jersey-2160/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2160</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2160</name>
+
+    <description>Servlet integration test - JERSEY-2160 - HttpServletRequest/HttpServletResponse do not get injected as a proxy into method parameters</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/Issue2160ReproducerResource.java b/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/Issue2160ReproducerResource.java
new file mode 100644
index 0000000..2097ef2
--- /dev/null
+++ b/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/Issue2160ReproducerResource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2160;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.glassfish.hk2.api.ProxyCtl;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("servletInjectees")
+public class Issue2160ReproducerResource {
+
+    @Context
+    HttpServletRequest requestField;
+    @Context
+    HttpServletResponse responseField;
+
+    @GET
+    @Produces("text/plain")
+    public String ensureNoProxyInjected(@Context HttpServletRequest requestParam, @Context HttpServletResponse responseParam) {
+
+        // make sure the injectees are same no matter how they got injected
+        if (requestParam != requestField || responseParam != responseField) {
+            throw new IllegalArgumentException("injected field and parameter should refer to the same instance");
+        }
+
+        // make sure we have not got proxies
+        if (requestParam instanceof ProxyCtl || responseParam instanceof ProxyCtl) {
+            throw new IllegalArgumentException("no proxy expected!");
+        }
+
+        return (String) requestParam.getAttribute(RequestFilter.REQUEST_NUMBER_PROPERTY);
+    }
+}
diff --git a/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/Jersey2160App.java b/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/Jersey2160App.java
new file mode 100644
index 0000000..29f21c6
--- /dev/null
+++ b/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/Jersey2160App.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2160;
+
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import java.util.HashSet;
+
+/**
+ * JAX-RS application for the JERSEY-1960 reproducer test.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Jersey2160App extends Application {
+
+    @SuppressWarnings({"unchecked"})
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {
+            {
+                add(RequestFilter.class);
+                add(Issue2160ReproducerResource.class);
+            }
+        };
+    }
+}
diff --git a/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/RequestFilter.java b/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/RequestFilter.java
new file mode 100644
index 0000000..8a44f0c
--- /dev/null
+++ b/tests/integration/jersey-2160/src/main/java/org/glassfish/jersey/tests/integration/jersey2160/RequestFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2160;
+
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Filter that set a property on actual request. The property value
+ * is expected to be propagated to the resource method via injected
+ * {@link HttpServletRequest} parameter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@PreMatching
+public class RequestFilter implements ContainerRequestFilter {
+
+    public static final String REQUEST_NUMBER_PROPERTY = "request-number";
+    public static final String REQUEST_NUMBER_HEADER = "number";
+
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException {
+        final String number = ctx.getHeaderString(REQUEST_NUMBER_HEADER);
+        ctx.setProperty(REQUEST_NUMBER_PROPERTY, number);
+    }
+}
diff --git a/tests/integration/jersey-2160/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2160/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3b97459
--- /dev/null
+++ b/tests/integration/jersey-2160/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2160Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2160.Jersey2160App</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2160Servlet</servlet-name>
+        <url-pattern>/jersey-2160/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2160/src/test/java/org/glassfish/jersey/tests/integration/jersey2160/Jersey2160ITCase.java b/tests/integration/jersey-2160/src/test/java/org/glassfish/jersey/tests/integration/jersey2160/Jersey2160ITCase.java
new file mode 100644
index 0000000..dd7f0b5
--- /dev/null
+++ b/tests/integration/jersey-2160/src/test/java/org/glassfish/jersey/tests/integration/jersey2160/Jersey2160ITCase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2160;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer tests for JERSEY-2160.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Jersey2160ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2160App();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Reproducer method for JERSEY-2160.
+     */
+    @Test
+    public void testJersey2160Fix() {
+        for (int i = 0; i < 10; i++) {
+            String response = target().path("jersey-2160/servletInjectees")
+                    .request().header(RequestFilter.REQUEST_NUMBER_HEADER, i)
+                    .get(String.class);
+            assertEquals(Integer.parseInt(response), i);
+        }
+    }
+}
diff --git a/tests/integration/jersey-2164/pom.xml b/tests/integration/jersey-2164/pom.xml
new file mode 100644
index 0000000..24f6782
--- /dev/null
+++ b/tests/integration/jersey-2164/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2164</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2164</name>
+
+    <description>Servlet integration test - JERSEY-2164 - Multi value http headers are not correctly read by the server</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2164/src/main/java/org/glassfish/jersey/tests/integration/jersey2164/Issue2164Resource.java b/tests/integration/jersey-2164/src/main/java/org/glassfish/jersey/tests/integration/jersey2164/Issue2164Resource.java
new file mode 100644
index 0000000..40b9da4
--- /dev/null
+++ b/tests/integration/jersey-2164/src/main/java/org/glassfish/jersey/tests/integration/jersey2164/Issue2164Resource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2164;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+
+/**
+ * Test resource.
+ *
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class Issue2164Resource {
+
+    @GET
+    public String get(@HeaderParam("hello") final List<String> headers) {
+        return headers.size() + ":" + headers;
+    }
+}
diff --git a/tests/integration/jersey-2164/src/main/java/org/glassfish/jersey/tests/integration/jersey2164/Jersey2164.java b/tests/integration/jersey-2164/src/main/java/org/glassfish/jersey/tests/integration/jersey2164/Jersey2164.java
new file mode 100644
index 0000000..0b4cff4
--- /dev/null
+++ b/tests/integration/jersey-2164/src/main/java/org/glassfish/jersey/tests/integration/jersey2164/Jersey2164.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2164;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2164 reproducer test.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2164 extends ResourceConfig {
+
+    public Jersey2164() {
+        register(Issue2164Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-2164/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2164/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..8b77a93
--- /dev/null
+++ b/tests/integration/jersey-2164/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2164Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2164.Jersey2164</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2164Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2164/src/test/java/org/glassfish/jersey/tests/integration/jersey2164/Jersey2164ITCase.java b/tests/integration/jersey-2164/src/test/java/org/glassfish/jersey/tests/integration/jersey2164/Jersey2164ITCase.java
new file mode 100644
index 0000000..79c24c9
--- /dev/null
+++ b/tests/integration/jersey-2164/src/test/java/org/glassfish/jersey/tests/integration/jersey2164/Jersey2164ITCase.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2164;
+
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2164.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2164ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2164();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHeaderListSingleHeader() throws Exception {
+        Response response = target().request().header("hello", "world").header("hello", "universe").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("1:[world,universe]"));
+    }
+
+    /**
+     * Check that multi value http headers are correctly read by the server.
+     */
+    @Test
+    public void testHeaderListMultipleHeaders() throws Exception {
+        final URL url = new URL(getBaseUri().toString());
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        connection.setRequestMethod("GET");
+        connection.setRequestProperty("Accept", "text/plain");
+        connection.setRequestProperty("hello", "world");
+        connection.addRequestProperty("hello", "universe");
+
+        connection.setDoOutput(false);
+        connection.connect();
+
+        assertThat(connection.getResponseCode(), equalTo(200));
+        assertThat(ReaderWriter.readFromAsString(new InputStreamReader(connection.getInputStream())),
+                equalTo("2:[world, universe]"));
+    }
+}
diff --git a/tests/integration/jersey-2167/pom.xml b/tests/integration/jersey-2167/pom.xml
new file mode 100644
index 0000000..943e894
--- /dev/null
+++ b/tests/integration/jersey-2167/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2167</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2167</name>
+
+    <description>JERSEY-2167 reproducer - Injecting a custom parameter type causes REST method to run twice</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/Issue2167Resource.java b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/Issue2167Resource.java
new file mode 100644
index 0000000..48ccef8
--- /dev/null
+++ b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/Issue2167Resource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2167;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Test resource for JERSEY-2167 reproducer.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+
+@Path("/MyResource")
+public class Issue2167Resource {
+    private static final Logger LOGGER = Logger.getLogger(Issue2167Resource.class.getName());
+    public static final String MSG = "Resource method doA() called at ";
+
+    @Path("test")
+    @GET
+    public Response doA(@MyAnnotation String param) {
+        LOGGER.log(Level.INFO, MSG + System.currentTimeMillis() + "; param=" + param);
+        if (param == null) {
+            return Response.serverError().build();
+        }
+        return Response.ok().build();
+    }
+}
diff --git a/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/Jersey2167App.java b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/Jersey2167App.java
new file mode 100644
index 0000000..1adc3cc
--- /dev/null
+++ b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/Jersey2167App.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2167;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2167 reproducer test.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class Jersey2167App extends ResourceConfig {
+
+    public Jersey2167App() {
+        register(Issue2167Resource.class);
+        register(new MyBinder());
+    }
+}
diff --git a/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyAnnotation.java b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyAnnotation.java
new file mode 100644
index 0000000..edec66e
--- /dev/null
+++ b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyAnnotation.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2167;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Custom annotation for JERSEY-2167 reproducer test.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MyAnnotation {
+}
diff --git a/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyBinder.java b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyBinder.java
new file mode 100644
index 0000000..b6de2c8
--- /dev/null
+++ b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyBinder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2167;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
+
+/**
+ * Custom annotation binder for JERSEY-2167 reproducer.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class MyBinder extends AbstractBinder {
+
+    @Override
+    protected void configure() {
+        bind(MyValueParamProvider.class).to(ValueParamProvider.class).in(Singleton.class);
+    }
+}
diff --git a/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyValueParamProvider.java b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyValueParamProvider.java
new file mode 100644
index 0000000..5eae5ae
--- /dev/null
+++ b/tests/integration/jersey-2167/src/main/java/org/glassfish/jersey/tests/integration/jersey2167/MyValueParamProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2167;
+
+import java.util.function.Function;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.internal.inject.AbstractValueParamProvider;
+import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
+import org.glassfish.jersey.server.model.Parameter;
+
+/**
+ * Custom annotation value supplier provider for JERSEY-2167 reproducer.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Singleton
+public class MyValueParamProvider extends AbstractValueParamProvider {
+
+    @Inject
+    public MyValueParamProvider(Provider<MultivaluedParameterExtractorProvider> mpep) {
+        super(mpep, Parameter.Source.UNKNOWN);
+    }
+
+    @Override
+    protected Function<ContainerRequest, ?> createValueProvider(Parameter parameter) {
+        return new MyValueSupplier();
+    }
+
+    private static final class MyValueSupplier implements Function<ContainerRequest, Object> {
+
+        @Override
+        public Object apply(ContainerRequest request) {
+            // returns some testing value
+            return "injected timestamp=" + System.currentTimeMillis();
+        }
+    }
+}
diff --git a/tests/integration/jersey-2167/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2167/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..be1fb9d
--- /dev/null
+++ b/tests/integration/jersey-2167/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2167Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2167.Jersey2167App</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2167Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2167/src/test/java/org/glassfish/jersey/tests/integration/jersey2167/Jersey2167ITCase.java b/tests/integration/jersey-2167/src/test/java/org/glassfish/jersey/tests/integration/jersey2167/Jersey2167ITCase.java
new file mode 100644
index 0000000..cff58c6
--- /dev/null
+++ b/tests/integration/jersey-2167/src/test/java/org/glassfish/jersey/tests/integration/jersey2167/Jersey2167ITCase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2167;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer tests for JERSEY-2167.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class Jersey2167ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new Jersey2167App();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testResourceMethodCall() throws Exception {
+        Response response = target().path("MyResource/test").request().get();
+        // if parameter was not injected, resource returns Response.Status.SERVER_ERROR (500). Missing parameter means,
+        // that hk2 injected the parameter and invoked the method preliminary and during Jersey-driven invocation,
+        // there was no parameter injection any more. If parameter was injected and Response.Status.OK (200) returned,
+        // the resource method was called only once (by Jersey).
+        assertEquals("Parameter not injected into resource method. Resource method could have been called twice. ",
+                response.getStatus(), Response.Status.OK.getStatusCode());
+    }
+}
diff --git a/tests/integration/jersey-2176/pom.xml b/tests/integration/jersey-2176/pom.xml
new file mode 100644
index 0000000..c4c3877
--- /dev/null
+++ b/tests/integration/jersey-2176/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2176</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2176</name>
+
+    <description>Servlet integration test - JERSEY-2176 - Problems with calling close() and flush()
+        methods on underlying output stream.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Issue2176ReproducerResource.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Issue2176ReproducerResource.java
new file mode 100644
index 0000000..6b0d093
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Issue2176ReproducerResource.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+/**
+ * Test resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("/resource")
+public class Issue2176ReproducerResource {
+
+    static final String X_FAIL_HEADER = "X-FAIL";
+    static final String X_RESPONSE_ENTITY_HEADER = "X-RESPONSE-ENTITY";
+
+    @GET
+    @Produces("text/plain")
+    @Path("{status}")
+    public Response getText(@PathParam("status") int uc,
+                            @Context HttpHeaders headers) throws MyException {
+        if (uc == -1) {
+            throw new MyException("UC= " + uc);
+        } else if (uc == -2) {
+            throw new IllegalStateException("UC= " + uc);
+        } else if (uc == -3) {
+            throw new WebApplicationException("UC= " + uc, 321);
+        } else if (uc == -4) {
+            throw new WebApplicationException("UC= " + uc, 432);
+        }
+
+        final Response.ResponseBuilder responseBuilder = Response.status(uc);
+        if (headers.getRequestHeaders().containsKey(X_RESPONSE_ENTITY_HEADER)) {
+            responseBuilder.entity("ENTITY");
+        }
+        final Response response = responseBuilder.build();
+        if (headers.getRequestHeaders().containsKey(X_RESPONSE_ENTITY_HEADER)) {
+            response.getHeaders().add(X_RESPONSE_ENTITY_HEADER, "true");
+        }
+        if (headers.getRequestHeaders().containsKey(X_FAIL_HEADER)) {
+            response.getHeaders().add(X_FAIL_HEADER, "true");
+        }
+        return response;
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java
new file mode 100644
index 0000000..e28cfc2
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176App.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * JAX-RS application for the JERSEY-2176 reproducer test.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+abstract class Jersey2176App extends ResourceConfig {
+
+    private final boolean setStatusOverSendError;
+
+    public Jersey2176App(boolean setStatusOverSendError) {
+        this.setStatusOverSendError = setStatusOverSendError;
+
+        property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, setStatusOverSendError);
+        register(MyWriterInterceptor.class);
+        register(Issue2176ReproducerResource.class);
+    }
+
+    public boolean isSetStatusOverSendError() {
+        return setStatusOverSendError;
+    }
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SendErrorApp.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SendErrorApp.java
new file mode 100644
index 0000000..dcdcaed
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SendErrorApp.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.ws.rs.ApplicationPath;
+
+/**
+ * Configure {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR} by {@code true} -
+ * method {@link javax.servlet.http.HttpServletResponse#sendError} will be called in case of errors
+ * (status {@code 4xx} or {@code 5xx}).
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath(Jersey2176SendErrorApp.APP_PATH)
+public class Jersey2176SendErrorApp extends Jersey2176App {
+
+    public static final String APP_PATH = "send-error";
+
+    public Jersey2176SendErrorApp() {
+        super(false);
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SetStatusApp.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SetStatusApp.java
new file mode 100644
index 0000000..501df02
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SetStatusApp.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.ws.rs.ApplicationPath;
+
+/**
+ * Configure {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR} by {@code true} -
+ * method {@link javax.servlet.http.HttpServletResponse#setStatus} will be called in case of errors
+ * (status {@code 4xx} or {@code 5xx}).
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath(Jersey2176SetStatusApp.APP_PATH)
+public class Jersey2176SetStatusApp extends Jersey2176App {
+
+    public static final String APP_PATH = "set-status";
+
+    public Jersey2176SetStatusApp() {
+        super(true);
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/MyException.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/MyException.java
new file mode 100644
index 0000000..e7daf21
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/MyException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class MyException extends Exception {
+
+    public MyException(String message) {
+        super(message);
+    }
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/MyWriterInterceptor.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/MyWriterInterceptor.java
new file mode 100644
index 0000000..f4a4fcd
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/MyWriterInterceptor.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+
+/**
+ * This just set new context output stream and test a clone method on set output stream instance is called.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class MyWriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException {
+        final boolean fail = context.getHeaders().containsKey(Issue2176ReproducerResource.X_FAIL_HEADER);
+        final boolean responseEntity = context.getHeaders().containsKey(Issue2176ReproducerResource.X_RESPONSE_ENTITY_HEADER);
+
+        if (responseEntity) {
+            context.setOutputStream(
+                    new MyOutputStream(context.getOutputStream(), MessageUtils.getCharset(context.getMediaType())));
+        }
+        context.proceed();
+        if (fail) {
+            throw new IllegalStateException("From MyWriterInterceptor");
+        }
+    }
+
+    private static class MyOutputStream extends OutputStream {
+        private final OutputStream delegate;
+        final Charset charset;
+        private final ByteArrayOutputStream localStream;
+
+        private MyOutputStream(final OutputStream delegate, final Charset charset) throws IOException {
+            this.delegate = delegate;
+            this.charset = charset;
+
+            this.localStream = new ByteArrayOutputStream();
+            localStream.write("[INTERCEPTOR]".getBytes(charset));
+        }
+
+        @Override
+        public void write(final int b) throws IOException {
+            localStream.write(b);
+        }
+
+        @Override
+        public void flush() throws IOException {
+            delegate.write(localStream.toByteArray());
+            localStream.reset();
+            delegate.flush();
+        }
+
+        @Override
+        public void close() throws IOException {
+            localStream.write("[/INTERCEPTOR]".getBytes(charset));
+
+            delegate.write(localStream.toByteArray());
+
+            delegate.close();
+            localStream.close();
+        }
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java
new file mode 100644
index 0000000..c20ecf3
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseFilter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import java.io.IOException;
+
+import javax.ws.rs.core.HttpHeaders;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TraceResponseFilter implements Filter {
+
+    public static final String X_SERVER_DURATION_HEADER = "X-SERVER-DURATION";
+    public static final String X_STATUS_HEADER = "X-STATUS";
+    public static final String X_NO_FILTER_HEADER = "X-NO-FILTER";
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    public void doFilter(final ServletRequest request, ServletResponse response, final FilterChain chain)
+            throws IOException, ServletException {
+        TraceResponseWrapper wrappedResponse = null;
+        if (((HttpServletRequest) request).getHeader(X_NO_FILTER_HEADER) == null) {
+            response = wrappedResponse = new TraceResponseWrapper((HttpServletResponse) response);
+        }
+        String status = "n/a";
+        final long startTime = System.nanoTime();
+        try {
+            chain.doFilter(request, response);
+            status = "OK";
+        } catch (final Throwable th) {
+            status = "FAIL";
+        } finally {
+            final long duration = System.nanoTime() - startTime;
+            ((HttpServletResponse) response).addHeader(X_SERVER_DURATION_HEADER, String.valueOf(duration));
+            ((HttpServletResponse) response).addHeader(X_STATUS_HEADER, status);
+            if (wrappedResponse != null) {
+                ((HttpServletResponse) response).setHeader(HttpHeaders.CONTENT_LENGTH, wrappedResponse.getContentLength());
+                wrappedResponse.writeBodyAndClose(response.getCharacterEncoding());
+            }
+        }
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java
new file mode 100644
index 0000000..3e2f672
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/java/org/glassfish/jersey/tests/integration/jersey2176/TraceResponseWrapper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TraceResponseWrapper extends HttpServletResponseWrapper {
+    private final ByteArrayOutputStream localStream;
+
+    public TraceResponseWrapper(final HttpServletResponse response) throws IOException {
+        super(response);
+
+        localStream = new ByteArrayOutputStream();
+        localStream.write("[FILTER]".getBytes(response.getCharacterEncoding()));
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException {
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+        return new ServletOutputStream() {
+            @Override
+            public void write(final int b) throws IOException {
+                localStream.write(b);
+            }
+        };
+    }
+
+    public void writeBodyAndClose(final String encoding) throws IOException {
+        localStream.write("[/FILTER]".getBytes(encoding));
+
+        super.getOutputStream().write(localStream.toByteArray());
+        super.getOutputStream().close();
+        localStream.close();
+    }
+
+    public String getContentLength() {
+        return String.valueOf(localStream.size() + "[/FILTER]".length());
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2176/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ac9109f
--- /dev/null
+++ b/tests/integration/jersey-2176/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>TraceFilter</filter-name>
+        <filter-class>org.glassfish.jersey.tests.integration.jersey2176.TraceResponseFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>TraceFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ITCaseBase.java b/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ITCaseBase.java
new file mode 100644
index 0000000..ffeec36
--- /dev/null
+++ b/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176ITCaseBase.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Reproducer tests for JERSEY-2176.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public abstract class Jersey2176ITCaseBase extends JerseyTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testGetContent222() {
+        testGetContent(222, true);
+    }
+
+    @Test
+    public void testGetContent333() {
+        testGetContent(333, true);
+    }
+
+    @Test
+    public void testGetContent444() {
+        testGetContent(444, true);
+    }
+
+    @Test
+    public void testGetContent555() {
+        testGetContent(555, true);
+    }
+
+    @Test
+    public void testGetContent222NoResponseEntity() {
+        testGetContent(222, false);
+    }
+
+    @Test
+    public void testGetContent333NoResponseEntity() {
+        testGetContent(333, false);
+    }
+
+    @Test
+    public void testGetContent444NoResponseEntity() {
+        testGetContent(444, false);
+    }
+
+    @Test
+    public void testGetContent555NoResponseEntity() {
+        testGetContent(555, false);
+    }
+
+    @Test
+    public void testGetException_1() {
+        testGetException(-1, 500, false);
+    }
+
+    @Test
+    public void testGetException_2() {
+        testGetException(-2, 500, false);
+    }
+
+    @Test
+    public void testGetException_3() {
+        testGetException(-3, 321, false);
+    }
+
+    @Test
+    public void testGetException_4() {
+        testGetException(-4, 432, false);
+    }
+
+    @Test
+    public void testGetException222() {
+        testGetException(222, 500, true);
+    }
+
+    private void testGetContent(int uc, boolean responseEntity) {
+        String expectedContent = "ENTITY";
+        expectedContent = "[INTERCEPTOR]" + expectedContent + "[/INTERCEPTOR]";
+        expectedContent = "[FILTER]" + expectedContent + "[/FILTER]";
+
+        Invocation.Builder builder = target().path("/resource/" + uc).request();
+        if (responseEntity) {
+            builder.header(Issue2176ReproducerResource.X_RESPONSE_ENTITY_HEADER, true);
+        } else {
+            builder.header(TraceResponseFilter.X_NO_FILTER_HEADER, true);
+        }
+
+        final Response response = builder.get();
+        final String assertMessage = uc + "|" + responseEntity;
+
+        Assert.assertEquals(assertMessage, uc, response.getStatus());
+        if (!sendErrorExpected(uc, responseEntity)) {
+            Assert.assertEquals(assertMessage, "OK", response.getHeaderString(TraceResponseFilter.X_STATUS_HEADER));
+            Assert.assertNotNull(assertMessage, response.getHeaderString(TraceResponseFilter.X_SERVER_DURATION_HEADER));
+            if (responseEntity) {
+                Assert.assertEquals(assertMessage, expectedContent, response.readEntity(String.class));
+                Assert.assertEquals(assertMessage, String.valueOf(expectedContent.length()),
+                        response.getHeaderString(HttpHeaders.CONTENT_LENGTH));
+            }
+        } else {
+            Assert.assertNull(assertMessage, response.getHeaderString(TraceResponseFilter.X_STATUS_HEADER));
+            Assert.assertNull(assertMessage, response.getHeaderString(TraceResponseFilter.X_SERVER_DURATION_HEADER));
+        }
+    }
+
+    private void testGetException(int uc, int expectedStatus, boolean fail) {
+        Invocation.Builder builder = target().path("/resource/" + uc).request();
+        builder = builder.header(Issue2176ReproducerResource.X_RESPONSE_ENTITY_HEADER, true);
+        if (fail) {
+            builder = builder.header(Issue2176ReproducerResource.X_FAIL_HEADER, true);
+        }
+
+        final Response response = builder.get();
+
+        final String expectedContent = "[FILTER][/FILTER]";
+        final String assertMessage = uc + ":" + expectedStatus + "|" + fail;
+
+        Assert.assertEquals(assertMessage, expectedStatus, response.getStatus());
+        if (!sendErrorExpected(expectedStatus, false)) {
+            Assert.assertEquals(assertMessage, expectedStatus == 500 ? "FAIL" : "OK",
+                    response.getHeaderString(TraceResponseFilter.X_STATUS_HEADER));
+            Assert.assertNotNull(assertMessage, response.getHeaderString(TraceResponseFilter.X_SERVER_DURATION_HEADER));
+            Assert.assertEquals(assertMessage, String.valueOf(expectedContent.length()),
+                    response.getHeaderString(HttpHeaders.CONTENT_LENGTH));
+        } else {
+            Assert.assertNull(assertMessage, response.getHeaderString(TraceResponseFilter.X_STATUS_HEADER));
+            Assert.assertNull(assertMessage, response.getHeaderString(TraceResponseFilter.X_SERVER_DURATION_HEADER));
+        }
+    }
+
+    private boolean sendErrorExpected(final int uc, final boolean responseEntity) {
+        return !((Jersey2176App) configure()).isSetStatusOverSendError() && (uc >= 400) && !responseEntity;
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SendErrorITCase.java b/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SendErrorITCase.java
new file mode 100644
index 0000000..9eb2648
--- /dev/null
+++ b/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SendErrorITCase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
+
+/**
+ * Reproducer tests for JERSEY-2176.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Jersey2176SendErrorITCase extends Jersey2176ITCaseBase {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2176SendErrorApp();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path(Jersey2176SendErrorApp.APP_PATH).build();
+    }
+
+}
diff --git a/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SetStatusITCase.java b/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SetStatusITCase.java
new file mode 100644
index 0000000..f894ee0
--- /dev/null
+++ b/tests/integration/jersey-2176/src/test/java/org/glassfish/jersey/tests/integration/jersey2176/Jersey2176SetStatusITCase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2176;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
+
+/**
+ * Reproducer tests for JERSEY-2176.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Jersey2176SetStatusITCase extends Jersey2176ITCaseBase {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2176SetStatusApp();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path(Jersey2176SetStatusApp.APP_PATH).build();
+    }
+
+}
diff --git a/tests/integration/jersey-2184/pom.xml b/tests/integration/jersey-2184/pom.xml
new file mode 100644
index 0000000..255156d
--- /dev/null
+++ b/tests/integration/jersey-2184/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2184</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2184</name>
+
+    <description>
+        Servlet integration test - JERSEY-2184 - ServletContext injection into Application subclass constructor.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2184/src/main/java/org/glassfish/jersey/tests/integration/jersey2184/App.java b/tests/integration/jersey-2184/src/main/java/org/glassfish/jersey/tests/integration/jersey2184/App.java
new file mode 100644
index 0000000..04a279f
--- /dev/null
+++ b/tests/integration/jersey-2184/src/main/java/org/glassfish/jersey/tests/integration/jersey2184/App.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2184;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Test Application subclass for JERSEY-2184 integration test.
+ *
+ * Tests the ability to inject {@link ServletContext} into application subclass constructor
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class App extends Application {
+
+    /** constructor-injected servletContext */
+    private ServletContext ctx;
+
+    public App(@Context ServletContext servletContext) {
+        this.ctx = servletContext;
+    }
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        HashSet<Class<?>> classes = new HashSet<>();
+        String dynamicClassName = ctx.getInitParameter("dynamicClassName");
+        Class<?> clazz = null;
+        if (classes.isEmpty()) {
+            try {
+                clazz = Class.forName(dynamicClassName);
+            } catch (ClassNotFoundException e) {
+                // swallow the exception - if class is not loaded, the integration test will fail
+            }
+
+            if (clazz != null) {
+                classes.add(clazz);
+            }
+        }
+        return classes;
+    }
+}
diff --git a/tests/integration/jersey-2184/src/main/java/org/glassfish/jersey/tests/integration/jersey2184/MonkeyResource.java b/tests/integration/jersey-2184/src/main/java/org/glassfish/jersey/tests/integration/jersey2184/MonkeyResource.java
new file mode 100644
index 0000000..bd9356b
--- /dev/null
+++ b/tests/integration/jersey-2184/src/main/java/org/glassfish/jersey/tests/integration/jersey2184/MonkeyResource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2184;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource for the servlet3-webapp example.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("monkey")
+public class MonkeyResource {
+    @GET
+    @Produces("text/plain")
+    public String chatter() {
+        return "Oooh!";
+    }
+}
diff --git a/tests/integration/jersey-2184/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2184/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..98b165c
--- /dev/null
+++ b/tests/integration/jersey-2184/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.jersey2184.App</servlet-name>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.jersey2184.App</servlet-name>
+        <url-pattern>/zoo/*</url-pattern>
+    </servlet-mapping>
+    <context-param>
+        <description>Context parameter to test the correct ServletContext injection into Application subclass.</description>
+        <param-name>dynamicClassName</param-name>
+        <param-value>org.glassfish.jersey.tests.integration.jersey2184.MonkeyResource</param-value>
+    </context-param>
+</web-app>
+
diff --git a/tests/integration/jersey-2184/src/test/java/org/glassfish/jersey/tests/integration/jersey2184/Jersey2184ITCase.java b/tests/integration/jersey-2184/src/test/java/org/glassfish/jersey/tests/integration/jersey2184/Jersey2184ITCase.java
new file mode 100644
index 0000000..794203d
--- /dev/null
+++ b/tests/integration/jersey-2184/src/test/java/org/glassfish/jersey/tests/integration/jersey2184/Jersey2184ITCase.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.jersey2184;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the JERSEY-2184 fix (the ability to inject ServletContext into application subclass constructor).
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class Jersey2184ITCase extends JerseyTest {
+
+    @Before
+    public void setup() {
+        Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy());
+    }
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        return new Application();  // dummy Application instance for the test framework - will no be used.
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        return UriBuilder.fromUri(super.getBaseUri()).path("zoo").build();
+    }
+
+    /**
+     * Tests if {@link javax.servlet.ServletContext} has been correctly injected into {@link App} constructor parameter;
+     * The resource under this URL is being loaded dynamically based on a context parameter in the web.xml,
+     * so if injection fails, the resource will not be available.
+     */
+    @Test
+    public void testInjection() {
+        String s = target().path("monkey").request().get(String.class);
+        assertEquals("Oooh!", s);
+    }
+}
diff --git a/tests/integration/jersey-2255/pom.xml b/tests/integration/jersey-2255/pom.xml
new file mode 100644
index 0000000..3b1f2ad
--- /dev/null
+++ b/tests/integration/jersey-2255/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2255</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2255</name>
+
+    <description>Filtering hierarchy test - JERSEY-2255 - Entity Data Filtering should support inherited fields</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+        	<groupId>org.glassfish.jersey.ext</groupId>
+        	<artifactId>jersey-entity-filtering</artifactId>
+        </dependency>
+        <dependency>
+        	<groupId>org.glassfish.jersey.media</groupId>
+        	<artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2255/src/main/java/org/glassfish/jersey/tests/integration/jersey2255/Issue2255Resource.java b/tests/integration/jersey-2255/src/main/java/org/glassfish/jersey/tests/integration/jersey2255/Issue2255Resource.java
new file mode 100644
index 0000000..0ac2f37
--- /dev/null
+++ b/tests/integration/jersey-2255/src/main/java/org/glassfish/jersey/tests/integration/jersey2255/Issue2255Resource.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2255;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Test resource.
+ *
+ * @author Eric Miles (emilesvt at gmail.com)
+ */
+@Path("/")
+@Consumes("application/json")
+@Produces("application/json")
+public class Issue2255Resource {
+
+    public static class A {
+
+        public A() {
+        }
+
+        public A(String fieldA1) {
+            this.fieldA1 = fieldA1;
+        }
+
+        private String fieldA1;
+
+        @Detailed
+        public String getFieldA1() {
+            return fieldA1;
+        }
+
+        public void setFieldA1(final String fieldA1) {
+            this.fieldA1 = fieldA1;
+        }
+    }
+
+    public static class B extends A {
+
+        public B() {
+        }
+
+        public B(String fieldA1, String fieldB1) {
+            super(fieldA1);
+            this.fieldB1 = fieldB1;
+        }
+
+        private String fieldB1;
+
+        public String getFieldB1() {
+            return fieldB1;
+        }
+
+        public void setFieldB1(final String fieldB1) {
+            this.fieldB1 = fieldB1;
+        }
+    }
+
+    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @Documented
+    @EntityFiltering
+    public static @interface Detailed {
+
+        /**
+         * Factory class for creating instances of {@code ProjectDetailedView} annotation.
+         */
+        public static class Factory
+                extends AnnotationLiteral<Detailed>
+                implements Detailed {
+
+            private Factory() {
+            }
+
+            public static Detailed get() {
+                return new Factory();
+            }
+        }
+    }
+
+    @Path("A")
+    @GET
+    public Response getA(@QueryParam("detailed") final boolean isDetailed) {
+        return Response
+                .ok()
+                .entity(new A("fieldA1Value"), isDetailed ? new Annotation[] {Detailed.Factory.get()} : new Annotation[0])
+                .build();
+    }
+
+    @Path("B")
+    @GET
+    public Response getB(@QueryParam("detailed") final boolean isDetailed) {
+        return Response
+                .ok()
+                .entity(new B("fieldA1Value", "fieldB1Value"),
+                        isDetailed ? new Annotation[] {Detailed.Factory.get()} : new Annotation[0])
+                .build();
+    }
+
+}
diff --git a/tests/integration/jersey-2255/src/main/java/org/glassfish/jersey/tests/integration/jersey2255/Jersey2255.java b/tests/integration/jersey-2255/src/main/java/org/glassfish/jersey/tests/integration/jersey2255/Jersey2255.java
new file mode 100644
index 0000000..a1dd1b3
--- /dev/null
+++ b/tests/integration/jersey-2255/src/main/java/org/glassfish/jersey/tests/integration/jersey2255/Jersey2255.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2255;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2255 reproducer test.
+ *
+ * @author Eric Miles (emilesvt at gmail.com)
+ */
+public class Jersey2255 extends ResourceConfig {
+
+    public Jersey2255() {
+        register(Issue2255Resource.class);
+        register(EntityFilteringFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2255/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2255/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..b6b050c
--- /dev/null
+++ b/tests/integration/jersey-2255/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2255Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2255.Jersey2255</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2255Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2255/src/test/java/org/glassfish/jersey/tests/integration/jersey2255/Jersey2255ITCase.java b/tests/integration/jersey-2255/src/test/java/org/glassfish/jersey/tests/integration/jersey2255/Jersey2255ITCase.java
new file mode 100644
index 0000000..a996eba
--- /dev/null
+++ b/tests/integration/jersey-2255/src/test/java/org/glassfish/jersey/tests/integration/jersey2255/Jersey2255ITCase.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2255;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.jersey2255.Issue2255Resource.A;
+import org.glassfish.jersey.tests.integration.jersey2255.Issue2255Resource.B;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Reproducer tests for JERSEY-2255.
+ *
+ * @author Eric Miles (emilesvt at gmail.com)
+ */
+public class Jersey2255ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return new Jersey2255();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Server side response is wrapped, needs to be read to wrapper class.
+     */
+    @Test
+    public void testClassAGet() {
+        final Response response = target("A").request().get();
+        final A entity = response.readEntity(A.class);
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertNull(entity.getFieldA1());
+    }
+
+    @Test
+    public void testDetailedClassAGet() {
+        final Response response = target("A").queryParam("detailed", true).request().get();
+        final A entity = response.readEntity(A.class);
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(entity.getFieldA1(), equalTo("fieldA1Value"));
+    }
+
+    /**
+     * Server side response is returned as orig class.
+     */
+    @Test
+    public void testDetailedClassBGet() {
+        final Response response = target("B").queryParam("detailed", true).request().get();
+        final B entity = response.readEntity(B.class);
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(entity.getFieldA1(), equalTo("fieldA1Value"));
+        assertThat(entity.getFieldB1(), equalTo("fieldB1Value"));
+    }
+
+    @Test
+    public void testClassBGet() {
+        final Response response = target("B").request().get();
+        final B entity = response.readEntity(B.class);
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertNull(entity.getFieldA1());
+        assertThat(entity.getFieldB1(), equalTo("fieldB1Value"));
+    }
+}
diff --git a/tests/integration/jersey-2322/pom.xml b/tests/integration/jersey-2322/pom.xml
new file mode 100644
index 0000000..a64aa6b
--- /dev/null
+++ b/tests/integration/jersey-2322/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2322</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2322</name>
+
+    <description>Servlet integration test - JERSEY-2322 - No way to configure auto-discovered Jackson 2.x ObjectMapper</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-metainf-services</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/Issue2322Resource.java b/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/Issue2322Resource.java
new file mode 100644
index 0000000..7e8b05f
--- /dev/null
+++ b/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/Issue2322Resource.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2322;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("/")
+@Consumes("application/json")
+@Produces("application/json")
+public class Issue2322Resource {
+
+    public static class JsonString1 {
+        private String value;
+
+        public JsonString1() {
+        }
+        public JsonString1(String value) {
+            this.value = value;
+        }
+        public String getValue() {
+            return value;
+        }
+        public void setValue(String value) {
+            this.value = value;
+        }
+    }
+
+    public static class JsonString2 {
+        private String value;
+
+        public JsonString2() {
+        }
+        public JsonString2(String value) {
+            this.value = value;
+        }
+        public String getValue() {
+            return value;
+        }
+        public void setValue(String value) {
+            this.value = value;
+        }
+    }
+
+    @Path("1")
+    @PUT
+    public JsonString1 put(final JsonString1 wrapper) {
+        return new JsonString1("Hello " + wrapper.getValue());
+    }
+
+    @Path("2")
+    @PUT
+    public JsonString2 put(final JsonString2 wrapper) {
+        return new JsonString2("Hi " + wrapper.getValue());
+    }
+
+}
diff --git a/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/Jersey2322.java b/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/Jersey2322.java
new file mode 100644
index 0000000..1e79c1e
--- /dev/null
+++ b/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/Jersey2322.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2322;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2322 reproducer test.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Jersey2322 extends ResourceConfig {
+
+    public Jersey2322() {
+        register(Issue2322Resource.class);
+        register(JacksonFeature.class);
+        register(MyObjectMapperProvider.class);
+    }
+}
diff --git a/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/MyObjectMapperProvider.java b/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/MyObjectMapperProvider.java
new file mode 100644
index 0000000..61fe632
--- /dev/null
+++ b/tests/integration/jersey-2322/src/main/java/org/glassfish/jersey/tests/integration/jersey2322/MyObjectMapperProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2322;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
+
+    @Override
+    public ObjectMapper getContext(Class<?> type) {
+        if (type == Issue2322Resource.JsonString1.class) {
+            ObjectMapper result = new ObjectMapper();
+            result.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
+            result.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, false);
+            return result;
+        } else {
+            return new ObjectMapper();
+        }
+    }
+}
diff --git a/tests/integration/jersey-2322/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2322/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..727ad27
--- /dev/null
+++ b/tests/integration/jersey-2322/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2322Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2322.Jersey2322</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2322Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2322/src/test/java/org/glassfish/jersey/tests/integration/jersey2322/Jersey2322ITCase.java b/tests/integration/jersey-2322/src/test/java/org/glassfish/jersey/tests/integration/jersey2322/Jersey2322ITCase.java
new file mode 100644
index 0000000..4f973a9
--- /dev/null
+++ b/tests/integration/jersey-2322/src/test/java/org/glassfish/jersey/tests/integration/jersey2322/Jersey2322ITCase.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2322;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2322.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Jersey2322ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return new Jersey2322();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Server side response is wrapped, needs to be read to wrapper class.
+     */
+    @Test
+    public void testJackson2JsonPut1() {
+        final Response response = target("1").request().put(Entity.json(new Issue2322Resource.JsonString1("foo")));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(Wrapper1.class).getJsonString1().getValue(), equalTo("Hello foo"));
+    }
+
+    /**
+     * Server side response is returned as orig class.
+     */
+    @Test
+    public void testJackson2JsonPut2() {
+        final Response response = target("2").request().put(Entity.json(new Issue2322Resource.JsonString2("foo")));
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(Issue2322Resource.JsonString2.class).getValue(), equalTo("Hi foo"));
+    }
+
+
+    public static class Wrapper1 {
+        @JsonProperty("JsonString1")
+        Issue2322Resource.JsonString1 jsonString1;
+
+        public Issue2322Resource.JsonString1 getJsonString1() {
+            return jsonString1;
+        }
+
+        public void setJsonString1(Issue2322Resource.JsonString1 jsonString1) {
+            this.jsonString1 = jsonString1;
+        }
+    }
+
+}
diff --git a/tests/integration/jersey-2335/pom.xml b/tests/integration/jersey-2335/pom.xml
new file mode 100644
index 0000000..075bed8
--- /dev/null
+++ b/tests/integration/jersey-2335/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2335</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2335</name>
+
+    <description>Servlet integration test - JERSEY-2335 - Providers do not get injected properly</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-metainf-services</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/ConstructorInjectedProvider.java b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/ConstructorInjectedProvider.java
new file mode 100644
index 0000000..7f59d01
--- /dev/null
+++ b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/ConstructorInjectedProvider.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2335;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+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 javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * Constructor injected provider to prove that provider registered via meta-inf/services
+ * mechanism gets constructed via HK2 and so the constructor parameters are properly injected.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@Produces("text/ctor-injected")
+public class ConstructorInjectedProvider implements MessageBodyWriter<String> {
+
+    Providers providers;
+
+    public ConstructorInjectedProvider(@Context final Providers providers) {
+        this.providers = providers;
+    }
+
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type == String.class;
+    }
+
+    @Override
+    public long getSize(final String t, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final String 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 MessageBodyWriter<String> plainTextWriter =
+                providers.getMessageBodyWriter(String.class, genericType, annotations, MediaType.TEXT_PLAIN_TYPE);
+        entityStream.write("via ctor injected provider:".getBytes(MessageUtils.getCharset(mediaType)));
+        plainTextWriter.writeTo(t, type, genericType, annotations, mediaType, httpHeaders, entityStream);
+    }
+}
diff --git a/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/FieldInjectedProvider.java b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/FieldInjectedProvider.java
new file mode 100644
index 0000000..5c6bc08
--- /dev/null
+++ b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/FieldInjectedProvider.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2335;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+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 javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * Field injected provider to prove that provider registered via meta-inf/services
+ * mechanism gets injected via HK2.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@Produces("text/field-injected")
+public class FieldInjectedProvider implements MessageBodyWriter<String> {
+
+    @Context Providers providers;
+
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type == String.class;
+    }
+
+    @Override
+    public long getSize(final String t, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final String 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 MessageBodyWriter<String> plainTextWriter =
+                providers.getMessageBodyWriter(String.class, genericType, annotations, MediaType.TEXT_PLAIN_TYPE);
+        entityStream.write("via field injected provider:".getBytes(MessageUtils.getCharset(mediaType)));
+        plainTextWriter.writeTo(t, type, genericType, annotations, mediaType, httpHeaders, entityStream);
+    }
+}
diff --git a/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/Issue2335Resource.java b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/Issue2335Resource.java
new file mode 100644
index 0000000..c4a44f6
--- /dev/null
+++ b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/Issue2335Resource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2335;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource that provides a simple text response.
+ * The response text then gets serialized using message body writer
+ * specified by media type.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/text")
+public class Issue2335Resource {
+
+    @GET
+    @Produces("text/*")
+    public String get() {
+        return "Hey";
+    }
+}
diff --git a/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/Jersey2335.java b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/Jersey2335.java
new file mode 100644
index 0000000..10ff7e2
--- /dev/null
+++ b/tests/integration/jersey-2335/src/main/java/org/glassfish/jersey/tests/integration/jersey2335/Jersey2335.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2335;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2335 reproducer test.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Jersey2335 extends ResourceConfig {
+
+    public Jersey2335() {
+        register(Issue2335Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-2335/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter b/tests/integration/jersey-2335/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter
new file mode 100644
index 0000000..f5bca8b
--- /dev/null
+++ b/tests/integration/jersey-2335/src/main/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter
@@ -0,0 +1,2 @@
+org.glassfish.jersey.tests.integration.jersey2335.FieldInjectedProvider
+org.glassfish.jersey.tests.integration.jersey2335.ConstructorInjectedProvider
\ No newline at end of file
diff --git a/tests/integration/jersey-2335/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2335/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..26d8b4a
--- /dev/null
+++ b/tests/integration/jersey-2335/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2335Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2335.Jersey2335</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2335Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2335/src/test/java/org/glassfish/jersey/tests/integration/jersey2335/Jersey2335ITCase.java b/tests/integration/jersey-2335/src/test/java/org/glassfish/jersey/tests/integration/jersey2335/Jersey2335ITCase.java
new file mode 100644
index 0000000..dfa5732
--- /dev/null
+++ b/tests/integration/jersey-2335/src/test/java/org/glassfish/jersey/tests/integration/jersey2335/Jersey2335ITCase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2335;
+
+import java.net.ConnectException;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Reproducer tests for JERSEY-2335.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Jersey2335ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2335();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testGetFieldInjected() throws Exception {
+        final Response response = target().path("text").request().accept("text/field-injected").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("via field injected provider:Hey"));
+    }
+
+    @Test
+    public void testGetConstructorInjected() throws Exception {
+        final Response response = target().path("text").request().accept("text/ctor-injected").get();
+
+        assertThat(response.getStatus(), equalTo(200));
+        assertThat(response.readEntity(String.class), equalTo("via ctor injected provider:Hey"));
+    }
+}
diff --git a/tests/integration/jersey-2421/pom.xml b/tests/integration/jersey-2421/pom.xml
new file mode 100644
index 0000000..edae017
--- /dev/null
+++ b/tests/integration/jersey-2421/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2421</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2421</name>
+
+    <description>Client integration test - JERSEY-2421 - MultiPartFeature requires jersey-server to be on the class-path</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java b/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java
new file mode 100644
index 0000000..dc71185
--- /dev/null
+++ b/tests/integration/jersey-2421/src/test/java/org/glassfish/jersey/tests/integration/jersey2421/Jersey2421Test.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2421;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.message.internal.NullOutputStream;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2421.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2421Test {
+
+    private static class TestConnector implements Connector, ConnectorProvider {
+
+        @Override
+        public ClientResponse apply(final ClientRequest request) {
+            try {
+                request.setStreamProvider(new OutboundMessageContext.StreamProvider() {
+                    @Override
+                    public OutputStream getOutputStream(final int contentLength) throws IOException {
+                        return new NullOutputStream();
+                    }
+                });
+                request.writeEntity();
+
+                if (request.getHeaderString("Content-Type").contains("boundary")) {
+                    return new ClientResponse(Response.Status.OK, request);
+                }
+            } catch (final IOException ioe) {
+                // NOOP
+            }
+            return new ClientResponse(Response.Status.BAD_REQUEST, request);
+        }
+
+        @Override
+        public Future<?> apply(final ClientRequest request, final AsyncConnectorCallback callback) {
+            return null;
+        }
+
+        @Override
+        public String getName() {
+            return null;
+        }
+
+        @Override
+        public void close() {
+        }
+
+        @Override
+        public Connector getConnector(final Client client, final Configuration runtimeConfig) {
+            return this;
+        }
+    }
+
+    /**
+     * Test that multipart feature works on the client-side - custom connector checks presence of {@code boundary} parameter in
+     * the {@code Content-Type} header (the header is added to the request in MBW).
+     */
+    @Test
+    public void testMultiPartFeatureOnClient() throws Exception {
+        final Client client = ClientBuilder.newClient(new ClientConfig().connectorProvider(new TestConnector()))
+                .register(MultiPartFeature.class);
+
+        final MultiPart entity = new FormDataMultiPart()
+                .bodyPart(new FormDataBodyPart(FormDataContentDisposition.name("part").build(), "CONTENT"));
+
+        final Response response = client.target("http://localhost").request()
+                .post(Entity.entity(entity, MediaType.MULTIPART_FORM_DATA_TYPE));
+
+        assertThat(response.getStatus(), is(200));
+    }
+
+    /**
+     * Test that classes from jersey-server module cannot be loaded.
+     */
+    @Test(expected = ClassNotFoundException.class)
+    public void testLoadJerseyServerClass() throws Exception {
+        Class.forName("org.glassfish.jersey.server.ResourceConfig");
+    }
+}
diff --git a/tests/integration/jersey-2551/pom.xml b/tests/integration/jersey-2551/pom.xml
new file mode 100644
index 0000000..c3fd8fa
--- /dev/null
+++ b/tests/integration/jersey-2551/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2551</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2551</name>
+
+    <description>Servlet integration test - JERSEY-2551 - Injections#generator field is hard coded to ServiceLocatorGeneratorImpl</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/Jersey2551.java b/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/Jersey2551.java
new file mode 100644
index 0000000..13f3277
--- /dev/null
+++ b/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/Jersey2551.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2551;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2551 reproducer test.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2551 extends ResourceConfig {
+
+    public Jersey2551() {
+        register(Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/Resource.java b/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/Resource.java
new file mode 100644
index 0000000..04dbe89
--- /dev/null
+++ b/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/Resource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2551;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+
+import org.glassfish.hk2.api.ServiceLocator;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class Resource {
+
+    @Inject
+    private ServiceLocator locator;
+
+    @GET
+    public Response get() {
+        return locator instanceof ServiceLocatorGenerator.CustomServiceLocator
+            ? Response.ok().build() : Response.serverError().build();
+    }
+}
diff --git a/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/ServiceLocatorGenerator.java b/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/ServiceLocatorGenerator.java
new file mode 100644
index 0000000..610ba43
--- /dev/null
+++ b/tests/integration/jersey-2551/src/main/java/org/glassfish/jersey/tests/integration/jersey2551/ServiceLocatorGenerator.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2551;
+
+import javax.inject.Singleton;
+
+import org.glassfish.hk2.api.DynamicConfigurationService;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.hk2.utilities.BuilderHelper;
+
+import org.jvnet.hk2.external.generator.ServiceLocatorGeneratorImpl;
+import org.jvnet.hk2.internal.DefaultClassAnalyzer;
+import org.jvnet.hk2.internal.DynamicConfigurationImpl;
+import org.jvnet.hk2.internal.DynamicConfigurationServiceImpl;
+import org.jvnet.hk2.internal.ServiceLocatorImpl;
+import org.jvnet.hk2.internal.Utilities;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ServiceLocatorGenerator extends ServiceLocatorGeneratorImpl {
+
+    @Override
+    public ServiceLocator create(final String name, final ServiceLocator parent) {
+        if (parent != null && !(parent instanceof ServiceLocatorImpl)) {
+            throw new AssertionError("parent must be a " + ServiceLocatorImpl.class.getName()
+                    + " instead it is a " + parent.getClass().getName());
+        }
+
+        final ServiceLocatorImpl sli = new CustomServiceLocator(name, (ServiceLocatorImpl) parent);
+
+        final DynamicConfigurationImpl dci = new DynamicConfigurationImpl(sli);
+
+        // The service locator itself
+        dci.bind(Utilities.getLocatorDescriptor(sli));
+
+        // The injection resolver for three thirty
+        dci.addActiveDescriptor(Utilities.getThreeThirtyDescriptor(sli));
+
+        // The dynamic configuration utility
+        dci.bind(BuilderHelper.link(DynamicConfigurationServiceImpl.class, false)
+                .to(DynamicConfigurationService.class)
+                .in(Singleton.class.getName())
+                .localOnly()
+                .build());
+
+        dci.bind(BuilderHelper.createConstantDescriptor(
+                new DefaultClassAnalyzer(sli)));
+
+        dci.commit();
+
+        return sli;
+    }
+
+    class CustomServiceLocator extends ServiceLocatorImpl {
+
+        public CustomServiceLocator(final String name, final ServiceLocatorImpl parent) {
+            super(name, parent);
+        }
+    }
+}
diff --git a/tests/integration/jersey-2551/src/main/resources/META-INF/services/org.glassfish.hk2.extension.ServiceLocatorGenerator b/tests/integration/jersey-2551/src/main/resources/META-INF/services/org.glassfish.hk2.extension.ServiceLocatorGenerator
new file mode 100644
index 0000000..ad70f6f
--- /dev/null
+++ b/tests/integration/jersey-2551/src/main/resources/META-INF/services/org.glassfish.hk2.extension.ServiceLocatorGenerator
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.integration.jersey2551.ServiceLocatorGenerator
\ No newline at end of file
diff --git a/tests/integration/jersey-2551/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2551/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ea1bd52
--- /dev/null
+++ b/tests/integration/jersey-2551/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2551Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2551.Jersey2551</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2551Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2551/src/test/java/org/glassfish/jersey/tests/integration/jersey2551/Jersey2551ITCase.java b/tests/integration/jersey-2551/src/test/java/org/glassfish/jersey/tests/integration/jersey2551/Jersey2551ITCase.java
new file mode 100644
index 0000000..a45e1b4
--- /dev/null
+++ b/tests/integration/jersey-2551/src/test/java/org/glassfish/jersey/tests/integration/jersey2551/Jersey2551ITCase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2551;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2551.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2551ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2551();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testServiceLocatorServer() throws Exception {
+        assertThat(target().request().get().getStatus(), is(200));
+    }
+}
diff --git a/tests/integration/jersey-2612/pom.xml b/tests/integration/jersey-2612/pom.xml
new file mode 100644
index 0000000..6fe55ac
--- /dev/null
+++ b/tests/integration/jersey-2612/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2612</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2612</name>
+
+    <description>Servlet integration test - JERSEY-2612 - SingleValueExtractor makes it impossible to support java.util.Optional (or Guava Optional).</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/Jersey2612.java b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/Jersey2612.java
new file mode 100644
index 0000000..edb6796
--- /dev/null
+++ b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/Jersey2612.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2612;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2612 reproducer test.
+ */
+public class Jersey2612 extends ResourceConfig {
+
+    public Jersey2612() {
+        register(Resource.class);
+        register(OptionalParamFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamBinder.java b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamBinder.java
new file mode 100644
index 0000000..a8808f6
--- /dev/null
+++ b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamBinder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2612;
+
+import javax.ws.rs.ext.ParamConverterProvider;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+
+final class OptionalParamBinder extends AbstractBinder {
+
+    @Override
+    protected void configure() {
+        // Param converter providers
+        bind(OptionalParamConverterProvider.class).to(ParamConverterProvider.class).in(Singleton.class);
+    }
+}
diff --git a/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamConverterProvider.java b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamConverterProvider.java
new file mode 100644
index 0000000..25b6edf
--- /dev/null
+++ b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamConverterProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2612;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.Providers;
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+import org.glassfish.jersey.internal.util.collection.ClassTypePair;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+
+@Singleton
+public class OptionalParamConverterProvider implements ParamConverterProvider {
+
+    private final InjectionManager injectionManager;
+
+    @Inject
+    public OptionalParamConverterProvider(final InjectionManager injectionManager) {
+        this.injectionManager = injectionManager;
+    }
+
+    @Override
+    public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType, final Annotation[] annotations) {
+        final List<ClassTypePair> ctps = ReflectionHelper.getTypeArgumentAndClass(genericType);
+        final ClassTypePair ctp = (ctps.size() == 1) ? ctps.get(0) : null;
+        if (ctp == null || ctp.rawClass() == String.class) {
+            return new ParamConverter<T>() {
+                @Override
+                public T fromString(final String value) {
+                    return rawType.cast(Optional.fromNullable(value));
+                }
+
+                @Override
+                public String toString(final T value) throws IllegalArgumentException {
+                    return value.toString();
+                }
+            };
+        }
+        final Set<ParamConverterProvider> converterProviders =
+                Providers.getProviders(injectionManager, ParamConverterProvider.class);
+        for (ParamConverterProvider provider : converterProviders) {
+            @SuppressWarnings("unchecked")
+            final ParamConverter<?> converter = provider.getConverter(ctp.rawClass(), ctp.type(), annotations);
+            if (converter != null) {
+                return new ParamConverter<T>() {
+                    @Override
+                    public T fromString(final String value) {
+                        return rawType.cast(Optional.fromNullable(value)
+                                                    .transform((Function<String, Object>) s -> converter.fromString(value)));
+                    }
+
+                    @Override
+                    public String toString(final T value) throws IllegalArgumentException {
+                        return value.toString();
+                    }
+                };
+            }
+        }
+        return null;
+    }
+}
diff --git a/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamFeature.java b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamFeature.java
new file mode 100644
index 0000000..d6af4d1
--- /dev/null
+++ b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/OptionalParamFeature.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2612;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+
+public class OptionalParamFeature implements Feature {
+
+    @Override
+    public boolean configure(final FeatureContext context) {
+        context.register(new OptionalParamBinder());
+        return true;
+    }
+
+}
+
diff --git a/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/Resource.java b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/Resource.java
new file mode 100644
index 0000000..ad1be27
--- /dev/null
+++ b/tests/integration/jersey-2612/src/main/java/org/glassfish/jersey/tests/integration/jersey2612/Resource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2612;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+
+/**
+ * Test resource.
+ */
+@Path("/")
+public class Resource {
+
+    @Path("hello")
+    @GET
+    @Produces("text/plain")
+    public String hello(@QueryParam("name") final Optional<String> name) {
+        return "Hello " + name.or("World") + "!";
+    }
+
+    @Path("square")
+    @GET
+    @Produces("text/plain")
+    public int echo(@QueryParam("value") final Optional<Integer> value) {
+        return value.transform(new Function<Integer, Integer>() {
+            @Override
+            public Integer apply(final Integer integer) {
+                return integer * integer;
+            }
+        }).or(0);
+    }
+
+}
diff --git a/tests/integration/jersey-2612/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2612/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..f506807
--- /dev/null
+++ b/tests/integration/jersey-2612/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey2612Servlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2612.Jersey2612</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey2612Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2612/src/test/java/org/glassfish/jersey/tests/integration/jersey2612/Jersey2612ITCase.java b/tests/integration/jersey-2612/src/test/java/org/glassfish/jersey/tests/integration/jersey2612/Jersey2612ITCase.java
new file mode 100644
index 0000000..0ffbad5
--- /dev/null
+++ b/tests/integration/jersey-2612/src/test/java/org/glassfish/jersey/tests/integration/jersey2612/Jersey2612ITCase.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2612;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2612.
+ */
+public class Jersey2612ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2612();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testResourceMethodWithOptionalGivenNoQueryParam() throws Exception {
+        final Response response = target("/hello").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("Hello World!"));
+    }
+
+    @Test
+    public void testResourceMethodWithOptionalGivenQueryParam() throws Exception {
+        final Response response = target("/hello").queryParam("name", "Jersey").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("Hello Jersey!"));
+    }
+
+    @Test
+    public void testResourceMethodWithOptionalIntGivenNoQueryParam() throws Exception {
+        final Response response = target("/square").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("0"));
+    }
+
+    @Test
+    public void testResourceMethodWithOptionalIntGivenQueryParam() throws Exception {
+        final Response response = target("/square").queryParam("value", "42").request().get();
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("1764"));
+    }
+}
diff --git a/tests/integration/jersey-2637/pom.xml b/tests/integration/jersey-2637/pom.xml
new file mode 100644
index 0000000..8615792
--- /dev/null
+++ b/tests/integration/jersey-2637/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2637</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2637</name>
+
+    <description>
+        Servlet integration test - JERSEY-2637 - sensitive params can be exposed in logs even for POST requests.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637.java b/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637.java
new file mode 100644
index 0000000..5f216ee
--- /dev/null
+++ b/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2637;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS application for the JERSEY-2637 reproducer test.
+ */
+public class Jersey2637 extends ResourceConfig {
+
+    public Jersey2637() {
+        register(Resource.class);
+    }
+}
diff --git a/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/ParamEatingFilter.java b/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/ParamEatingFilter.java
new file mode 100644
index 0000000..d1522c6
--- /dev/null
+++ b/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/ParamEatingFilter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2637;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ParamEatingFilter implements Filter {
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+        // NOOP
+    }
+
+    @Override
+    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
+            throws IOException, ServletException {
+        // NOM-NOM.
+        request.getParameterMap();
+
+        // Continue.
+        chain.doFilter(request, response);
+    }
+
+    @Override
+    public void destroy() {
+        // NOOP
+    }
+}
diff --git a/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/Resource.java b/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/Resource.java
new file mode 100644
index 0000000..39401cf
--- /dev/null
+++ b/tests/integration/jersey-2637/src/main/java/org/glassfish/jersey/tests/integration/jersey2637/Resource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2637;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+/**
+ * Test resource.
+ */
+@Path("/")
+public class Resource {
+
+    @POST
+    public String params(@FormParam("username") @DefaultValue("ko") final String username,
+                         @FormParam("password") @DefaultValue("ko") final String password) {
+        return username + "_" + password;
+    }
+}
diff --git a/tests/integration/jersey-2637/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2637/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..051d43b
--- /dev/null
+++ b/tests/integration/jersey-2637/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>jersey2637Filter</filter-name>
+        <filter-class>org.glassfish.jersey.tests.integration.jersey2637.ParamEatingFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>jersey2637Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <servlet>
+        <servlet-name>default</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2637.Jersey2637</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>default</servlet-name>
+        <url-pattern>/default/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet>
+        <servlet-name>enabled</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2637.Jersey2637</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.form.queryParams.disabled</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>enabled</servlet-name>
+        <url-pattern>/enabled/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet>
+        <servlet-name>disabled</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2637.Jersey2637</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.form.queryParams.disabled</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>disabled</servlet-name>
+        <url-pattern>/disabled/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2637/src/test/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637DisabledITCase.java b/tests/integration/jersey-2637/src/test/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637DisabledITCase.java
new file mode 100644
index 0000000..de6b68e
--- /dev/null
+++ b/tests/integration/jersey-2637/src/test/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637DisabledITCase.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2637;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2637 - Query params cannot be injected using {@link javax.ws.rs.FormParam}.
+ */
+public class Jersey2637DisabledITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Jersey2637();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testFormParams() throws Exception {
+        final Form form = new Form()
+                .param("username", "user")
+                .param("password", "pass");
+
+        final Response response = target("disabled").request().post(Entity.form(form));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+
+    @Test
+    public void testQueryParams() throws Exception {
+        final Response response = target("disabled")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(new Form()));
+
+        assertThat(response.readEntity(String.class), is("ko_ko"));
+    }
+
+    @Test
+    public void testDoubleQueryParams() throws Exception {
+        final Response response = target("disabled")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(new Form()));
+
+        assertThat(response.readEntity(String.class), is("ko_ko"));
+    }
+
+    @Test
+    public void testEncodedQueryParams() throws Exception {
+        final Response response = target("disabled")
+                .queryParam("username", "us%20er").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(new Form()));
+
+        assertThat(response.readEntity(String.class), is("ko_ko"));
+    }
+
+    @Test
+    public void testFormAndQueryParams() throws Exception {
+        final Form form = new Form()
+                .param("username", "user")
+                .param("password", "pass");
+
+        final Response response = target("disabled")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(form));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+
+    @Test
+    public void testFormAndDoubleQueryParams() throws Exception {
+        final Form form = new Form()
+                .param("username", "user")
+                .param("password", "pass");
+
+        final Response response = target("disabled")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(form));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+}
diff --git a/tests/integration/jersey-2637/src/test/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637EnabledITCase.java b/tests/integration/jersey-2637/src/test/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637EnabledITCase.java
new file mode 100644
index 0000000..1240bc1
--- /dev/null
+++ b/tests/integration/jersey-2637/src/test/java/org/glassfish/jersey/tests/integration/jersey2637/Jersey2637EnabledITCase.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2637;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Reproducer tests for JERSEY-2637 - Query params can be injected using {@link javax.ws.rs.FormParam}.
+ */
+@RunWith(Parameterized.class)
+public class Jersey2637EnabledITCase extends JerseyTest {
+
+    @Parameterized.Parameters(name = "path = {0}")
+    public static Collection<Object[]> paths() {
+        return Arrays.asList(new Object[][]{{"defaut"}, {"enabled"}});
+    }
+
+    @Parameterized.Parameter
+    public String path;
+
+    @Override
+    protected Application configure() {
+        return new Jersey2637();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testFormParams() throws Exception {
+        final Form form = new Form()
+                .param("username", "user")
+                .param("password", "pass");
+
+        final Response response = target(path).request().post(Entity.form(form));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+
+    @Test
+    public void testQueryParams() throws Exception {
+        final Response response = target(path)
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(new Form()));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+
+    @Test
+    public void testDoubleQueryParams() throws Exception {
+        final Response response = target(path)
+                .queryParam("username", "user").queryParam("password", "pass")
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(new Form()));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+
+    @Test
+    public void testEncodedQueryParams() throws Exception {
+        final Response response = target(path)
+                .queryParam("username", "us%20er").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(new Form()));
+
+        assertThat(response.readEntity(String.class), is("us er_pass"));
+    }
+
+    @Test
+    public void testFormAndQueryParams() throws Exception {
+        final Form form = new Form()
+                .param("username", "user")
+                .param("password", "pass");
+
+        final Response response = target(path)
+                .queryParam("username", "user").queryParam("password", "pass")
+                .request()
+                .post(Entity.form(form));
+
+        assertThat(response.readEntity(String.class), is("user_pass"));
+    }
+}
diff --git a/tests/integration/jersey-2654/pom.xml b/tests/integration/jersey-2654/pom.xml
new file mode 100644
index 0000000..7ae7658
--- /dev/null
+++ b/tests/integration/jersey-2654/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2654</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2654</name>
+
+    <description>Jersey test web application - servlet/filter, JERSEY-2654 reproducer</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jersey-2654/src/main/java/org/glassfish/jersey/tests/integration/jersey2654/ServletFilterTestResource.java b/tests/integration/jersey-2654/src/main/java/org/glassfish/jersey/tests/integration/jersey2654/ServletFilterTestResource.java
new file mode 100644
index 0000000..bfc062b
--- /dev/null
+++ b/tests/integration/jersey-2654/src/main/java/org/glassfish/jersey/tests/integration/jersey2654/ServletFilterTestResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2654;
+
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+/**
+ * Test resource to be called within an ServletContainer registered as servlet filter.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("filter")
+public class ServletFilterTestResource {
+
+    @GET
+    public Response whatYouSendIsWhatYouGet(@DefaultValue("") @QueryParam("json") final String json) {
+        return Response.ok().entity(json).build();
+    }
+}
diff --git a/tests/integration/jersey-2654/src/main/java/org/glassfish/jersey/tests/integration/jersey2654/TestApplication.java b/tests/integration/jersey-2654/src/main/java/org/glassfish/jersey/tests/integration/jersey2654/TestApplication.java
new file mode 100644
index 0000000..099008f
--- /dev/null
+++ b/tests/integration/jersey-2654/src/main/java/org/glassfish/jersey/tests/integration/jersey2654/TestApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2654;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application to configure resources for JERSEY-2525 reproducer.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestApplication extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> classes = new HashSet<>();
+        classes.add(ServletFilterTestResource.class);
+        return classes;
+    }
+}
diff --git a/tests/integration/jersey-2654/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2654/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..a75c20f
--- /dev/null
+++ b/tests/integration/jersey-2654/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <filter>
+        <filter-name>MyApplication</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2654.TestApplication</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>MyApplication</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
+
+
diff --git a/tests/integration/jersey-2654/src/test/java/org/glassfish/jersey/tests/integration/jersey2654/Jersey2654ITCase.java b/tests/integration/jersey-2654/src/test/java/org/glassfish/jersey/tests/integration/jersey2654/Jersey2654ITCase.java
new file mode 100644
index 0000000..0589b64
--- /dev/null
+++ b/tests/integration/jersey-2654/src/test/java/org/glassfish/jersey/tests/integration/jersey2654/Jersey2654ITCase.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2654;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.Socket;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Reproducer for JERSEY-2654
+ *
+ * Tests, that unencoded curly brackets (typically used in URI queries containing JSON) do not cause the request to
+ * fail when running in a servlet environment and configured as a filter.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class Jersey2654ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testJsonInUriWithSockets() throws IOException {
+        // Low level approach with sockets is used, because common Java HTTP clients are using java.net.URI,
+        // which fails when unencoded curly bracket is part of the URI
+        final Socket socket = new Socket(getBaseUri().getHost(), getBaseUri().getPort());
+        final PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
+
+        // quotes are encoded by browsers, curly brackets are not, so the quotes will be sent pre-encoded
+        // HTTP 1.0 is used for simplicity
+        pw.println("GET /filter?json={%22foo%22:%22bar%22} HTTP/1.0");
+        pw.println();   // http request should end with a blank line
+        pw.flush();
+
+        final BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+        String lastLine = null;
+        String line;
+        while ((line = br.readLine()) != null) {
+            // read the response and remember the last line
+            lastLine = line;
+        }
+        assertEquals("{\"foo\":\"bar\"}", lastLine);
+        br.close();
+    }
+}
diff --git a/tests/integration/jersey-2673/pom.xml b/tests/integration/jersey-2673/pom.xml
new file mode 100644
index 0000000..827aa30
--- /dev/null
+++ b/tests/integration/jersey-2673/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2673</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2673</name>
+
+    <description>Jersey test web application - servlet/filter, JERSEY-2673 reproducer</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/Jersey2673.java b/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/Jersey2673.java
new file mode 100644
index 0000000..26f6274
--- /dev/null
+++ b/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/Jersey2673.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2673;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Jersey2673 extends ResourceConfig {
+
+    public Jersey2673() {
+        // Resources.
+        register(Resource.class);
+
+        // Providers.
+        register(JacksonFeature.class);
+
+        // Make sure validations errors are returned to the client.
+        property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
+    }
+}
diff --git a/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/Resource.java b/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/Resource.java
new file mode 100644
index 0000000..326a39e
--- /dev/null
+++ b/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/Resource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2673;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+import javax.validation.Valid;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/")
+@Produces("application/json")
+public class Resource {
+
+    @POST
+    @Valid
+    public Response processBean(@Valid final SampleBean bean) {
+        return Response.ok(new SampleBean()).build();
+    }
+}
diff --git a/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/SampleBean.java b/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/SampleBean.java
new file mode 100644
index 0000000..21ac029
--- /dev/null
+++ b/tests/integration/jersey-2673/src/main/java/org/glassfish/jersey/tests/integration/jersey2673/SampleBean.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2673;
+
+import org.hibernate.validator.constraints.NotEmpty;
+
+/**
+ * @author Michal Gajdos
+ */
+public class SampleBean {
+
+    @NotEmpty
+    private byte[] array;
+
+    public byte[] getArray() {
+        return array;
+    }
+
+    public void setArray(final byte[] array) {
+        this.array = array;
+    }
+}
diff --git a/tests/integration/jersey-2673/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2673/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..712879e
--- /dev/null
+++ b/tests/integration/jersey-2673/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5"
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>jersey</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2673.Jersey2673</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>jersey</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/tests/integration/jersey-2673/src/test/java/org/glassfish/jersey/tests/integration/jersey2673/Jersey2673ITCase.java b/tests/integration/jersey-2673/src/test/java/org/glassfish/jersey/tests/integration/jersey2673/Jersey2673ITCase.java
new file mode 100644
index 0000000..f157001
--- /dev/null
+++ b/tests/integration/jersey-2673/src/test/java/org/glassfish/jersey/tests/integration/jersey2673/Jersey2673ITCase.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2673;
+
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.validation.ValidationError;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Reproducer for JERSEY-2673: 400 errors generated by a ValidationException are returning default error page instead of
+ * the Bean Validation's response entity.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2673ITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new Jersey2673();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInputParams() throws Exception {
+        final Response response = target().request("application/json").post(Entity.json(new SampleBean()));
+
+        assertThat(response.getStatusInfo().getStatusCode(), is(Response.Status.BAD_REQUEST.getStatusCode()));
+        assertThat(response.readEntity(new GenericType<List<ValidationError>>() {}).size(), is(1));
+    }
+
+    @Test
+    public void testOutputParams() throws Exception {
+        final SampleBean entity = new SampleBean();
+        entity.setArray(new byte[] {42});
+        final Response response = target().request("application/json").post(Entity.json(entity));
+
+        assertThat(response.getStatusInfo().getStatusCode(), is(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()));
+        assertThat(response.readEntity(new GenericType<List<ValidationError>>() {}).size(), is(1));
+    }
+
+}
diff --git a/tests/integration/jersey-2689/pom.xml b/tests/integration/jersey-2689/pom.xml
new file mode 100644
index 0000000..c4583c8
--- /dev/null
+++ b/tests/integration/jersey-2689/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2689</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2689</name>
+
+    <description>Servlet integration test - JERSEY-2689 - Problem with validation errors on primitive type arrays</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/Jersey2689.java b/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/Jersey2689.java
new file mode 100644
index 0000000..fc18355
--- /dev/null
+++ b/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/Jersey2689.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2689;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+
+/**
+ * @author Oscar Guindzberg
+ */
+public class Jersey2689 extends ResourceConfig {
+
+    public Jersey2689() {
+        // Set package to look for resources in
+        packages("org.glassfish.jersey.tests.integration.jersey2689");
+
+        this.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
+
+        // create custom ObjectMapper
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(SerializationFeature.INDENT_OUTPUT);
+
+        // create JsonProvider to provide custom ObjectMapper
+        final JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
+        provider.setMapper(mapper);
+        this.register(provider);
+
+    }
+
+}
diff --git a/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/Resource.java b/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/Resource.java
new file mode 100644
index 0000000..6e97b9e
--- /dev/null
+++ b/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2689;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import javax.validation.Valid;
+
+@Path("/")
+public class Resource {
+
+    @POST
+    @Path("/post-bean")
+    public void processBean(@Valid final SampleBean bean) {
+        //do-nothing
+    }
+}
diff --git a/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/SampleBean.java b/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/SampleBean.java
new file mode 100644
index 0000000..a4e759d
--- /dev/null
+++ b/tests/integration/jersey-2689/src/main/java/org/glassfish/jersey/tests/integration/jersey2689/SampleBean.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2689;
+
+import org.hibernate.validator.constraints.NotEmpty;
+
+public class SampleBean {
+
+    @NotEmpty
+    private byte[] array;
+
+    public byte[] getArray() {
+        return array;
+    }
+
+    public void setArray(byte[] array) {
+        this.array = array;
+    }
+
+}
diff --git a/tests/integration/jersey-2689/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2689/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3842845
--- /dev/null
+++ b/tests/integration/jersey-2689/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app version="2.5"
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2689.Jersey2689</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/tests/integration/jersey-2689/src/test/java/org/glassfish/jersey/tests/integration/jersey2689/Jersey2689ITCase.java b/tests/integration/jersey-2689/src/test/java/org/glassfish/jersey/tests/integration/jersey2689/Jersey2689ITCase.java
new file mode 100644
index 0000000..2db2efa
--- /dev/null
+++ b/tests/integration/jersey-2689/src/test/java/org/glassfish/jersey/tests/integration/jersey2689/Jersey2689ITCase.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.jersey2689;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+
+/**
+ * Tests for JERSEY-2689: Problem with validation errors on primitive type arrays.
+ * <p/> There is a bug when a validation fails for a primitive data array. Eg a NotNull failed validation on a byte[] causes the code to throw a ClassCastException. The problem is caused by ValidationHelper.getViolationInvalidValue(Object invalidValue) It tries to cast any array to a Object[] A byte[] parameter would generate a ClassCastException.*
+ * @author Oscar Guindzberg (oscar.guindzberg at gmail.com)
+ */
+public class Jersey2689ITCase extends JerseyTest {
+
+
+    @Override
+    protected ResourceConfig configure() {
+        return new Jersey2689();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Checks if a thread gets stuck when an {@code IOException} is thrown from the {@code
+     * MessageBodyWriter#writeTo}.
+     */
+    @Test
+    public void testByteArray() throws Exception {
+        // Executor.
+        final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+        final Future<Response> responseFuture = executor.submit(new Callable<Response>() {
+
+            @Override
+            public Response call() throws Exception {
+                SampleBean bean = new SampleBean();
+                bean.setArray(new byte[]{});
+
+                ObjectMapper mapper = new ObjectMapper();
+                mapper.enable(SerializationFeature.INDENT_OUTPUT);
+                JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
+                provider.setMapper(mapper);
+                client().register(provider);
+
+                return target().path("post-bean").request().post(Entity.entity(bean, MediaType.APPLICATION_JSON));
+            }
+
+        });
+
+        executor.shutdown();
+        final boolean inTime = executor.awaitTermination(5000, TimeUnit.MILLISECONDS);
+
+        // Asserts.
+        assertTrue(inTime);
+
+        // Response.
+        final Response response = responseFuture.get();
+
+        //Make sure we get a 400 error and not a 500 error
+        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+    }
+
+
+}
diff --git a/tests/integration/jersey-2704/pom.xml b/tests/integration/jersey-2704/pom.xml
new file mode 100644
index 0000000..5dea360
--- /dev/null
+++ b/tests/integration/jersey-2704/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2704</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2704</name>
+
+    <description>
+        This test is to verify if ServiceLocator can be configured within the server context
+        in such a way that Jersey can use it as a parent ServiceLocator in the WebComponent.
+        More details can be found in JERSEY-2704.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/ServiceLocatorSetup.java b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/ServiceLocatorSetup.java
new file mode 100644
index 0000000..3eb8be8
--- /dev/null
+++ b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/ServiceLocatorSetup.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey2704;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
+import org.glassfish.jersey.servlet.ServletProperties;
+import org.glassfish.jersey.tests.integration.jersey2704.services.HappyService;
+
+
+/**
+ * This class is to listen for {@link ServletContextEvent} generated whenever context
+ * is initialized and set {@link ServletProperties#SERVICE_LOCATOR} attribute to point
+ * {@link ServiceLocator} pre-populated with {@link HappyService} instance.
+ *
+ * @author Bartosz Firyn (sarxos)
+ */
+public class ServiceLocatorSetup implements ServletContextListener {
+
+    @Override
+    public void contextInitialized(ServletContextEvent event) {
+        ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
+        ServiceLocatorUtilities.addOneConstant(locator, new HappyService());
+        event.getServletContext().setAttribute(ServletProperties.SERVICE_LOCATOR, locator);
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent event) {
+    }
+}
diff --git a/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/TestApplication.java b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/TestApplication.java
new file mode 100644
index 0000000..ac7bc57
--- /dev/null
+++ b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/TestApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey2704;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+
+/**
+ * Jersey application.
+ *
+ * @author Bartosz Firyn (bartoszfiryn at gmail.com)
+ */
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+    }
+}
diff --git a/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/TestResource.java b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/TestResource.java
new file mode 100644
index 0000000..dc07f94
--- /dev/null
+++ b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/TestResource.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey2704;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.internal.inject.InjectionManager;
+
+/**
+ * This resource is used to test if specific service class instance is available in the
+ * {@link InjectionManager} that comes from Jersey context.
+ *
+ * @author Bartosz Firyn (bartoszfiryn at gmail.com)
+ */
+@Path("test")
+public class TestResource {
+
+    InjectionManager injectionManager;
+
+    /**
+     * Inject {@link InjectionManager} from Jersey context.
+     *
+     * @param injectionManager the {@link InjectionManager}
+     */
+    @Inject
+    public TestResource(InjectionManager injectionManager) {
+        this.injectionManager = injectionManager;
+    }
+
+    /**
+     * This method will test given class by checking if it is available in {@link InjectionManager}
+     * that has been injected from the Jersey context.
+     *
+     * @param clazz the service class name to check
+     * @return {@link Response} with status code 200 if service is available, 600 otherwise
+     * @throws Exception in case when there are any error (e.g. class not exist)
+     */
+    @GET
+    @Path("{clazz}")
+    @Produces("text/plain")
+    public Response test(@PathParam("clazz") String clazz) throws Exception {
+        return Response
+            .status(injectionManager.getInstance(Class.forName(clazz)) != null ? 200 : 600)
+            .build();
+    }
+}
diff --git a/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/services/HappyService.java b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/services/HappyService.java
new file mode 100644
index 0000000..2c87d94
--- /dev/null
+++ b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/services/HappyService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey2704.services;
+
+import org.jvnet.hk2.annotations.Service;
+
+
+/**
+ * This service is registered in the {@link org.glassfish.jersey.internal.inject.InjectionManager} and therefore
+ * can be used in Jersey resources.
+ *
+ * @author Bartosz Firyn (bartoszfiryn at gmail.com)
+ */
+@Service
+public class HappyService {
+
+}
diff --git a/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/services/SadService.java b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/services/SadService.java
new file mode 100644
index 0000000..1d4df28
--- /dev/null
+++ b/tests/integration/jersey-2704/src/main/java/org/glassfish/jersey/tests/integration/jersey2704/services/SadService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey2704.services;
+
+import org.jvnet.hk2.annotations.Service;
+
+
+/**
+ * This service is not registered in {@link org.glassfish.jersey.internal.inject.InjectionManager} and therefore cannot
+ * be used in the Jersey resources.
+ *
+ * @author Bartosz Firyn (bartoszfiryn at gmail.com)
+ */
+@Service
+public class SadService {
+
+}
diff --git a/tests/integration/jersey-2704/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2704/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..44a2543
--- /dev/null
+++ b/tests/integration/jersey-2704/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <listener>
+        <listener-class>org.glassfish.jersey.tests.integration.jersey2704.ServiceLocatorSetup</listener-class>
+    </listener>
+    <servlet>
+        <servlet-name>test</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2704.TestApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>test</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-2704/src/test/java/org/glassfish/jersey/tests/integration/jersey2704/Jersey2704ITCase.java b/tests/integration/jersey-2704/src/test/java/org/glassfish/jersey/tests/integration/jersey2704/Jersey2704ITCase.java
new file mode 100644
index 0000000..2ed829e
--- /dev/null
+++ b/tests/integration/jersey-2704/src/test/java/org/glassfish/jersey/tests/integration/jersey2704/Jersey2704ITCase.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey2704;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.jersey2704.services.HappyService;
+import org.glassfish.jersey.tests.integration.jersey2704.services.SadService;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/**
+ * This test case is to cover enhancement implemented in JERSEY-2704. The goal of this enhancement
+ * is to give users possibility to register main {@link ServiceLocator} in the servlet context, so
+ * it can be later used by Jersey. This creates the opportunity to wire Jersey-specific classes with
+ * the services created outside the Jersey context.
+ *
+ * @author Bartosz Firyn (bartoszfiryn at gmail.com)
+ */
+public class Jersey2704ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Invokes REST endpoint to check whether specific class service is registered in the
+     * {@link ServiceLocator}.
+     *
+     * @param service the service class
+     * @return HTTP status code, 200 when service is available and 600 otherwise
+     * @throws IOException in case of problems with HTTP communication
+     */
+    private int test(Class<?> service) throws IOException {
+
+        String name = service.getCanonicalName();
+        String path = getBaseUri().toString() + "test/" + name;
+
+        HttpURLConnection connection = (HttpURLConnection) new URL(path).openConnection();
+        connection.setRequestMethod("GET");
+        connection.connect();
+        connection.disconnect();
+
+        return connection.getResponseCode();
+    }
+
+    /**
+     * Test to cover sunny day scenario, i.e. specific service has been registered in the parent
+     * {@link ServiceLocator} so it will be available in the one that is used in Jersey context.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void testCorrectInjection() throws IOException {
+        Assert.assertEquals(200, test(HappyService.class));
+    }
+
+    /**
+     * Test to cover rainy day scenario, i.e. specific service has <b>not</b> been registered in the
+     * parent {@link ServiceLocator} so it cannot be used to wire Jersey classes.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void testMisingInjection() throws IOException {
+        Assert.assertEquals(600, test(SadService.class));
+    }
+}
diff --git a/tests/integration/jersey-2776/pom.xml b/tests/integration/jersey-2776/pom.xml
new file mode 100644
index 0000000..0b484a9
--- /dev/null
+++ b/tests/integration/jersey-2776/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2776</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2776</name>
+
+    <description>
+        This test is to verify that multipart support (server side) works even if apache CXF's RuntimeDelegateImpl is used instead
+        of the RuntimeDelegateImpl shipped with Jersey.
+    </description>
+
+    <dependencies>
+        <!-- For testing multipart support with another http client -->
+        <!-- The order of these imports are important as it's the first dependency that has a javax.ws.rs.ext.RuntimeDelegate in
+             the services that will be used. -->
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-rs-client</artifactId>
+            <version>3.0.3</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2776/src/main/java/org/glassfish/jersey/tests/integration/jersey2776/TestApplication.java b/tests/integration/jersey-2776/src/main/java/org/glassfish/jersey/tests/integration/jersey2776/TestApplication.java
new file mode 100644
index 0000000..3739ba3
--- /dev/null
+++ b/tests/integration/jersey-2776/src/main/java/org/glassfish/jersey/tests/integration/jersey2776/TestApplication.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2776;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey application.
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+        register(MultiPartFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2776/src/main/java/org/glassfish/jersey/tests/integration/jersey2776/TestResource.java b/tests/integration/jersey-2776/src/main/java/org/glassfish/jersey/tests/integration/jersey2776/TestResource.java
new file mode 100644
index 0000000..1f05e91
--- /dev/null
+++ b/tests/integration/jersey-2776/src/main/java/org/glassfish/jersey/tests/integration/jersey2776/TestResource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2776;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+
+@Path("/files")
+public class TestResource {
+
+    @POST
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    @Produces(MediaType.TEXT_PLAIN)
+    public String uploadDocument(@FormDataParam("file_path") final FormDataBodyPart body) {
+        return body.getValueAs(String.class);
+    }
+}
diff --git a/tests/integration/jersey-2776/src/test/java/org/glassfish/jersey/tests/integration/jersey2776/Jersey2776ITCase.java b/tests/integration/jersey-2776/src/test/java/org/glassfish/jersey/tests/integration/jersey2776/Jersey2776ITCase.java
new file mode 100644
index 0000000..7a6d2af
--- /dev/null
+++ b/tests/integration/jersey-2776/src/test/java/org/glassfish/jersey/tests/integration/jersey2776/Jersey2776ITCase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2776;
+
+import java.nio.charset.StandardCharsets;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.multipart.internal.MultiPartReaderClientSide;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.AttachmentBuilder;
+import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Code under test: {@link MultiPartReaderClientSide} (unquoteMediaTypeParameters)
+ *
+ * @author Jonatan Jönsson (jontejj at gmail.com)
+ */
+@Ignore
+public class Jersey2776ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Path("/files")
+    public interface ApacheCxfMultipartClient {
+
+        @POST
+        @Consumes(MediaType.MULTIPART_FORM_DATA)
+        @Produces(MediaType.TEXT_PLAIN)
+        String uploadDocument(MultipartBody content);
+    }
+
+    @Test
+    public void testThatMultipartServerSupportsBoundaryQuotesEvenWithInterferingRuntimeDelegate() {
+        final JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+        bean.setAddress(getBaseUri().toString());
+        bean.setServiceClass(ApacheCxfMultipartClient.class);
+        final ApacheCxfMultipartClient cxfClient = bean.create(ApacheCxfMultipartClient.class);
+
+        final String originalContent = "abc";
+        final byte[] content = originalContent.getBytes(StandardCharsets.US_ASCII);
+        final Attachment fileAttachment = new AttachmentBuilder()
+                .object(content)
+                .contentDisposition(new ContentDisposition("form-data; filename=\"abc-file\"; name=\"file_path\""))
+                .build();
+
+        final String fileContentReturnedFromServer = cxfClient.uploadDocument(new MultipartBody(fileAttachment));
+        assertThat(fileContentReturnedFromServer, equalTo(originalContent));
+    }
+}
diff --git a/tests/integration/jersey-2794/pom.xml b/tests/integration/jersey-2794/pom.xml
new file mode 100644
index 0000000..f009641
--- /dev/null
+++ b/tests/integration/jersey-2794/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2794</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2794</name>
+
+    <description>
+        Reproducer of JERSEY-2794. This test makes sure that temporary files created via mimepull library are deleted right after
+        the request processing failed and not when the JVM is shut down.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2794/src/main/java/org/glassfish/jersey/tests/integration/jersey2794/TestApplication.java b/tests/integration/jersey-2794/src/main/java/org/glassfish/jersey/tests/integration/jersey2794/TestApplication.java
new file mode 100644
index 0000000..615df07
--- /dev/null
+++ b/tests/integration/jersey-2794/src/main/java/org/glassfish/jersey/tests/integration/jersey2794/TestApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2794;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey application for JERSEY-2794.
+ *
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+        register(MultiPartFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2794/src/main/java/org/glassfish/jersey/tests/integration/jersey2794/TestResource.java b/tests/integration/jersey-2794/src/main/java/org/glassfish/jersey/tests/integration/jersey2794/TestResource.java
new file mode 100644
index 0000000..aa8b89f
--- /dev/null
+++ b/tests/integration/jersey-2794/src/main/java/org/glassfish/jersey/tests/integration/jersey2794/TestResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2794;
+
+import java.io.InputStream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.media.multipart.FormDataParam;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class TestResource {
+
+    @PUT
+    @Consumes("multipart/form-data")
+    @Produces("text/plain")
+    public String put(@FormDataParam("stream") final InputStream stream) {
+        return "OK";
+    }
+}
diff --git a/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java b/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java
new file mode 100644
index 0000000..60a54ca
--- /dev/null
+++ b/tests/integration/jersey-2794/src/test/java/org/glassfish/jersey/tests/integration/jersey2794/Jersey2794ITCase.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2794;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * JERSEY-2794 reproducer.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2794ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void mimeTempFileRemoved() throws Exception {
+        final String tempDir = System.getProperty("java.io.tmpdir");
+
+        // Get number of matching MIME*tmp files (the number should be the same at the end of the test).
+        final int expectedTempFiles = matchingTempFiles(tempDir);
+
+        final URL url = new URL(getBaseUri().toString());
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        connection.setRequestMethod("PUT");
+        connection.setRequestProperty("Accept", "text/plain");
+        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=XXXX_YYYY");
+
+        connection.setDoOutput(true);
+        connection.connect();
+
+        final OutputStream outputStream = connection.getOutputStream();
+        outputStream.write("--XXXX_YYYY".getBytes());
+        outputStream.write('\n');
+        outputStream.write("Content-Type: text/plain".getBytes());
+        outputStream.write('\n');
+        outputStream.write("Content-Disposition: form-data; name=\"big-part\"".getBytes());
+        outputStream.write('\n');
+        outputStream.write('\n');
+
+        // Send big chunk of data.
+        for (int i = 0; i < 16 * 4096; i++) {
+            outputStream.write('E');
+            if (i % 1024 == 0) {
+                outputStream.flush();
+            }
+        }
+
+        // Do NOT send end of the MultiPart message to simulate the issue.
+
+        // Get Response ...
+        assertThat("Bad Request expected", connection.getResponseCode(), is(400));
+
+        // Make sure that the Mimepull message and it's parts have been closed and temporary files deleted.
+        assertThat("Temporary mimepull files were not deleted", matchingTempFiles(tempDir), is(expectedTempFiles));
+
+        // ... Disconnect.
+        connection.disconnect();
+    }
+
+    private int matchingTempFiles(final String tempDir) throws IOException {
+        AtomicInteger count = new AtomicInteger(0);
+        Files.walkFileTree(Paths.get(tempDir), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                if (file.getFileName().startsWith("MIME") && file.getFileName().endsWith("tmp")) {
+                    count.incrementAndGet();
+                }
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        return count.get();
+    }
+}
diff --git a/tests/integration/jersey-2846/pom.xml b/tests/integration/jersey-2846/pom.xml
new file mode 100644
index 0000000..7e54819
--- /dev/null
+++ b/tests/integration/jersey-2846/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2846</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2846</name>
+
+    <description>
+        Reproducer of JERSEY-2846. This test makes sure that temporary files created via mimepull (and moved by Jersey) library
+        are deleted right after the request processing (either successful or unsuccessful) and not when the JVM is shut down.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2846/src/main/java/org/glassfish/jersey/tests/integration/jersey2846/TestApplication.java b/tests/integration/jersey-2846/src/main/java/org/glassfish/jersey/tests/integration/jersey2846/TestApplication.java
new file mode 100644
index 0000000..42dafbe
--- /dev/null
+++ b/tests/integration/jersey-2846/src/main/java/org/glassfish/jersey/tests/integration/jersey2846/TestApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2846;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey application for JERSEY-2846.
+ *
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+        register(MultiPartFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2846/src/main/java/org/glassfish/jersey/tests/integration/jersey2846/TestResource.java b/tests/integration/jersey-2846/src/main/java/org/glassfish/jersey/tests/integration/jersey2846/TestResource.java
new file mode 100644
index 0000000..7356fcf
--- /dev/null
+++ b/tests/integration/jersey-2846/src/main/java/org/glassfish/jersey/tests/integration/jersey2846/TestResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2846;
+
+import java.io.File;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.media.multipart.FormDataParam;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/")
+@Consumes("multipart/form-data")
+@Produces("text/plain")
+public class TestResource {
+
+    @POST
+    @Path("ExceptionInMethod")
+    public String exceptionInMethod(@FormDataParam("file") final File file) {
+        throw new WebApplicationException(Response.serverError().entity(file.getAbsolutePath()).build());
+    }
+
+    @POST
+    @Path("SuccessfulMethod")
+    public String successfulMethod(@FormDataParam("file") final File file) {
+        return file.getAbsolutePath();
+    }
+}
diff --git a/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java b/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java
new file mode 100644
index 0000000..0aecaa2
--- /dev/null
+++ b/tests/integration/jersey-2846/src/test/java/org/glassfish/jersey/tests/integration/jersey2846/Jersey2846ITCase.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2846;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * JERSEY-2846 reproducer.
+ *
+ * @author Michal Gajdos
+ */
+public class Jersey2846ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(MultiPartFeature.class);
+    }
+
+    @Test
+    public void tempFileDeletedAfterSuccessfulProcessing() throws Exception {
+        _testSmall("SuccessfulMethod", 200);
+    }
+
+    @Test
+    public void tempFileDeletedAfterExceptionInMethod() throws Exception {
+        _testSmall("ExceptionInMethod", 500);
+    }
+
+    @Test
+    public void tempFileDeletedAfterSuccessfulProcessingBigEntity() throws Exception {
+        _testBig("SuccessfulMethod", 200);
+    }
+
+    @Test
+    public void tempFileDeletedAfterExceptionInMethodBigEntity() throws Exception {
+        _testBig("ExceptionInMethod", 500);
+    }
+
+    public void _testBig(final String path, final int status) throws Exception {
+        final byte[] array = new byte[8196];
+        Arrays.fill(array, (byte) 52);
+
+        _test(path, status, array);
+    }
+
+    public void _testSmall(final String path, final int status) throws Exception {
+        _test(path, status, "CONTENT");
+    }
+
+    public void _test(final String path, final int status, final Object entity) throws Exception {
+        final String tempDir = System.getProperty("java.io.tmpdir");
+
+        // Get number of matching MIME*tmp files (the number should be the same at the end of the test).
+        final int expectedTempFiles = matchingTempFiles(tempDir);
+
+        final FormDataMultiPart multipart = new FormDataMultiPart();
+        final FormDataBodyPart bodypart = new FormDataBodyPart(FormDataContentDisposition.name("file").fileName("file").build(),
+                entity, MediaType.TEXT_PLAIN_TYPE);
+        multipart.bodyPart(bodypart);
+
+        final Response response = target().path(path)
+                .request()
+                .post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA));
+
+        // Get Response ...
+        assertThat(response.getStatus(), is(status));
+        // Wait a second to make sure the files don't exist.
+        Thread.sleep(1000);
+
+        // Make sure that the message and it's parts have been closed and temporary files deleted.
+        assertThat("Temporary files were not deleted", matchingTempFiles(tempDir), is(expectedTempFiles));
+    }
+
+    private int matchingTempFiles(final String tempDir) throws IOException {
+        AtomicInteger count = new AtomicInteger(0);
+        Files.walkFileTree(Paths.get(tempDir), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Path name = file.getFileName();
+                if ((name.startsWith("rep") || name.startsWith("MIME")) && name.endsWith("tmp")) {
+                    count.incrementAndGet();
+                }
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        return count.get();
+    }
+}
diff --git a/tests/integration/jersey-2878/pom.xml b/tests/integration/jersey-2878/pom.xml
new file mode 100644
index 0000000..2cf6ae1
--- /dev/null
+++ b/tests/integration/jersey-2878/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2878</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2878</name>
+
+    <description>
+        Reproducer of JERSEY-2878.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2878/src/main/java/org/glassfish/jersey/tests/integration/jersey2878/TestApplication.java b/tests/integration/jersey-2878/src/main/java/org/glassfish/jersey/tests/integration/jersey2878/TestApplication.java
new file mode 100644
index 0000000..a7f533e
--- /dev/null
+++ b/tests/integration/jersey-2878/src/main/java/org/glassfish/jersey/tests/integration/jersey2878/TestApplication.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2878;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey application for JERSEY-2878.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+    }
+}
diff --git a/tests/integration/jersey-2878/src/main/java/org/glassfish/jersey/tests/integration/jersey2878/TestResource.java b/tests/integration/jersey-2878/src/main/java/org/glassfish/jersey/tests/integration/jersey2878/TestResource.java
new file mode 100644
index 0000000..eaada6a
--- /dev/null
+++ b/tests/integration/jersey-2878/src/main/java/org/glassfish/jersey/tests/integration/jersey2878/TestResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2878;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * A simple resource that returns a string.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("string")
+public class TestResource {
+
+    @GET
+    public String string() {
+        return "Hello, World! "
+                + "In production this response would be large, "
+                + "and unsuitable for buffering in memory";
+    }
+}
diff --git a/tests/integration/jersey-2878/src/test/java/org/glassfish/jersey/tests/integration/jersey2878/Jersey2878ApacheITCase.java b/tests/integration/jersey-2878/src/test/java/org/glassfish/jersey/tests/integration/jersey2878/Jersey2878ApacheITCase.java
new file mode 100644
index 0000000..8786b52
--- /dev/null
+++ b/tests/integration/jersey-2878/src/test/java/org/glassfish/jersey/tests/integration/jersey2878/Jersey2878ApacheITCase.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2878;
+
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+
+/**
+ * Additional test class that tests jersey client configured with the apache http.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class Jersey2878ApacheITCase extends Jersey2878ITCase {
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.connectorProvider(new ApacheConnectorProvider());
+
+        super.configureClient(config);
+    }
+}
diff --git a/tests/integration/jersey-2878/src/test/java/org/glassfish/jersey/tests/integration/jersey2878/Jersey2878ITCase.java b/tests/integration/jersey-2878/src/test/java/org/glassfish/jersey/tests/integration/jersey2878/Jersey2878ITCase.java
new file mode 100644
index 0000000..a31f49f
--- /dev/null
+++ b/tests/integration/jersey-2878/src/test/java/org/glassfish/jersey/tests/integration/jersey2878/Jersey2878ITCase.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2878;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+public class Jersey2878ITCase extends JerseyTest {
+
+    private List<InputStream> responseInputStreams = new ArrayList<>();
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        ClientResponseFilter trackInputStreams = new ClientResponseFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
+                responseInputStreams.add(responseContext.getEntityStream());
+            }
+        };
+
+        config.register(trackInputStreams);
+    }
+
+    private static void consumeStreamFully(InputStream inputStream) throws IOException {
+        while (inputStream.read() != -1) {
+            //consume the stream fully
+        }
+    }
+
+    @Test
+    public void thisShouldWorkButFails() throws Exception {
+        InputStream stream = target("string").request().get(InputStream.class);
+        try {
+            consumeStreamFully(stream);
+        } finally {
+            stream.close();
+        }
+
+        try {
+            stream.read();
+            fail("Exception was not thrown when read() was called on closed stream! Stream implementation: " + stream.getClass());
+        } catch (IOException e) {
+            // this is desired
+        }
+
+        assertThatAllInputStreamsAreClosed();
+    }
+
+    @Test
+    public void thisWorksButIsReallyUgly() throws Exception {
+        Response response = target("string").request().get();
+        if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
+            throw new RuntimeException("We have to manually check that the response was successful");
+        }
+        InputStream stream = response.readEntity(InputStream.class);
+        try {
+            consumeStreamFully(stream);
+        } finally {
+            response.close();
+        }
+
+        try {
+            stream.read();
+            fail("Exception was not thrown when read() was called on closed stream! Stream implementation: " + stream.getClass());
+        } catch (IOException e) {
+            // this is desired
+        }
+
+        assertThatAllInputStreamsAreClosed();
+    }
+
+    @Test
+    public void thisAlsoFails() throws Exception {
+        Response response = target("string").request().get();
+        if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
+            throw new RuntimeException("We have to manually check that the response was successful");
+        }
+
+        InputStream stream = response.readEntity(InputStream.class);
+        try {
+            consumeStreamFully(stream);
+        } finally {
+            stream.close();
+        }
+
+        try {
+            stream.read();
+            fail("Exception was not thrown when read() was called on closed stream! Stream implementation: " + stream.getClass());
+        } catch (IOException e) {
+            // this is desired
+        }
+
+        assertThatAllInputStreamsAreClosed();
+    }
+
+    @Test
+    public void worksWithACast_ifYouKnowThatYouCanCast() throws Exception {
+        Response response = target("string").request().get();
+        if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
+            throw new RuntimeException("We have to manually check that the response was successful");
+        }
+
+        InputStream stream = (InputStream) response.getEntity();
+        try {
+            consumeStreamFully(stream);
+        } finally {
+            stream.close();
+        }
+
+        try {
+            stream.read();
+            fail("Exception was not thrown when read() was called on closed stream! Stream implementation: " + stream.getClass());
+        } catch (IOException e) {
+            // this is desired
+        }
+
+        assertThatAllInputStreamsAreClosed();
+    }
+
+    private void assertThatAllInputStreamsAreClosed() {
+        if (responseInputStreams.size() == 0) {
+            fail("no input stream to check");
+        }
+        for (InputStream stream : responseInputStreams) {
+            assertClosed(stream);
+        }
+    }
+
+    private void assertClosed(InputStream stream) {
+        try {
+            byte[] buffer = new byte[256];
+            stream.read(buffer); //it's not ignored — we're checking for the exception
+            fail("Stream is not closed! Stream implementation: " + stream.getClass());
+        } catch (IOException e) {
+            // an exception is desired
+        }
+    }
+}
diff --git a/tests/integration/jersey-2892/pom.xml b/tests/integration/jersey-2892/pom.xml
new file mode 100644
index 0000000..1e4647f
--- /dev/null
+++ b/tests/integration/jersey-2892/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-2892</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-2892</name>
+
+    <description>
+        Reproducer of JERSEY-2892.
+
+        Repeating classes in object graph of entity filtering facility should not be always filtered out.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- which jersey media provider to use is determined in web.xml by using 'jersey.config.jsonFeature' init param -->
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestApplication.java b/tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestApplication.java
new file mode 100644
index 0000000..f1b5eb0
--- /dev/null
+++ b/tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2892;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey application for JERSEY-2878 and also J-605 (which is derived from the former one).
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+        register(EntityFilteringFeature.class);
+    }
+}
diff --git a/tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestResource.java b/tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestResource.java
new file mode 100644
index 0000000..c59b252
--- /dev/null
+++ b/tests/integration/jersey-2892/src/main/java/org/glassfish/jersey/tests/integration/jersey2892/TestResource.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2892;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlTransient;
+
+/**
+ * A resource that provides a means to test whether repeating classes in object graph are correctly filtered out.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/")
+@Produces(MediaType.APPLICATION_JSON)
+public class TestResource {
+
+    @GET
+    @Path("pointer")
+    public Pointer pointer() {
+        return new Pointer();
+    }
+
+    public static class Pointer {
+
+        public final Persons persons = new Persons();
+    }
+
+    @GET
+    @Path("test")
+    public Persons issue() {
+        return new Persons();
+    }
+
+    public static class Persons {
+
+        public final Person first = new Person("Larry", "Amphitheatre Pkwy", 1600, "Mountain View");
+        public final Person second = new Person("Bill", "Microsoft Way", 1, "Redmond");
+    }
+
+    public static class Person {
+
+        public Person() {
+        }
+
+        public Person(final String name, final String streetName, final int streetNumber, final String city) {
+            this.name = name;
+            address = new Address(streetName, streetNumber, city);
+        }
+
+        public Address address;
+        public String name;
+    }
+
+    public static class Address {
+
+        public Address() {
+        }
+
+        public Address(final String name, final int number, final String city) {
+            this.city = city;
+            street = new Street(name, number);
+        }
+
+        public Street street;
+        public String city;
+    }
+
+    public static class Street {
+
+        public Street() {
+        }
+
+        public Street(final String name, final int number) {
+            this.name = name;
+            this.number = number;
+        }
+
+        public String name;
+        public int number;
+    }
+
+    @GET
+    @Path("recursive")
+    public Recursive recursive() {
+        return new Recursive();
+    }
+
+    public static class Recursive {
+
+        public String idRecursive = "a";
+        public SubField subField = new SubField();
+    }
+
+    public static class SubField {
+
+        public final String idSubField = "b";
+        public final SubSubField subSubField;
+
+        public SubField() {
+            subSubField = new SubSubField(this);
+        }
+    }
+
+    public static class SubSubField {
+
+        public final String idSubSubField = "c";
+
+        @XmlTransient
+        public SubField subField;
+
+        public SubSubField() {
+        }
+
+        public SubSubField(final SubField subField) {
+            this.subField = subField;
+        }
+    }
+}
diff --git a/tests/integration/jersey-2892/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-2892/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d52a435
--- /dev/null
+++ b/tests/integration/jersey-2892/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>jerseyMoxyFilteringFeature</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2892.TestApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.jsonFeature</param-name>
+            <param-value>MoxyJsonFeature</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jerseyMoxyFilteringFeature</servlet-name>
+        <url-pattern>/moxy/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet>
+        <servlet-name>jerseyJacksonFilteringFeature</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey2892.TestApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.jsonFeature</param-name>
+            <param-value>JacksonFeature</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jerseyJacksonFilteringFeature</servlet-name>
+        <url-pattern>/jackson/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/AbstractJerseyEntityFilteringITCase.java b/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/AbstractJerseyEntityFilteringITCase.java
new file mode 100644
index 0000000..ac5a7ab
--- /dev/null
+++ b/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/AbstractJerseyEntityFilteringITCase.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2892;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests whether classes repeating in the object graph are filtered out correctly.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public abstract class AbstractJerseyEntityFilteringITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Tests whether sub-sub-field, {@link TestResource.Street} in particular,
+     * is not filtered out.
+     * <p/>
+     * This corresponds with the JERSEY-2892 reported case.
+     */
+    @Test
+    public void testWhetherSubSubFiledIsNotFilteredOut() {
+        Response response = target(provider() + "/test").request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        final TestResource.Persons persons = response.readEntity(TestResource.Persons.class);
+
+        Assert.assertEquals("Amphitheatre Pkwy", persons.first.address.street.name);
+        Assert.assertEquals("Microsoft Way", persons.second.address.street.name);
+    }
+
+    /**
+     * Tests whether a de-referenced case of the reported problem is still correctly not filtered out. In particular, a
+     * sub-sub-sub-field of the same class is not filtered out.
+     */
+    @Test
+    public void testWhetherSubSubSubFieldIsNotFilteredOut() {
+        Response response = target(provider() + "/pointer").request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        final TestResource.Pointer pointer = response.readEntity(TestResource.Pointer.class);
+
+        Assert.assertEquals("Amphitheatre Pkwy", pointer.persons.first.address.street.name);
+        Assert.assertEquals("Microsoft Way", pointer.persons.second.address.street.name);
+    }
+
+    /**
+     * Tests whether a reference cycle is detected and infinite recursion is prevented.
+     */
+    @Test
+    public void testWhetherReferenceCycleIsDetected() {
+        Response response = target(provider() + "/recursive").request(MediaType.APPLICATION_JSON_TYPE).get();
+
+        final TestResource.Recursive recursive = response.readEntity(TestResource.Recursive.class);
+
+        Assert.assertEquals("c", recursive.subField.subSubField.idSubSubField);
+    }
+
+    /**
+     * Jersey Entity filtering feature provider.
+     *
+     * @return The provider string to match with appropriate Jersey app configured in web.xml.
+     */
+    protected abstract String provider();
+
+}
diff --git a/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/J605MoxyITCase.java b/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/J605MoxyITCase.java
new file mode 100644
index 0000000..501df41
--- /dev/null
+++ b/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/J605MoxyITCase.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2892;
+
+/**
+ * Tests whether classes repeating in the object graph are filtered out correctly when using MOXY json provider.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class J605MoxyITCase extends AbstractJerseyEntityFilteringITCase {
+
+    @Override
+    protected String provider() {
+        return "moxy";
+    }
+}
diff --git a/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/Jersey2892JacksonITCase.java b/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/Jersey2892JacksonITCase.java
new file mode 100644
index 0000000..1ce858e
--- /dev/null
+++ b/tests/integration/jersey-2892/src/test/java/org/glassfish/jersey/tests/integration/jersey2892/Jersey2892JacksonITCase.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.jersey2892;
+
+/**
+ * Tests whether classes repeating in the object graph are filtered out correctly when using Jackson json provider.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class Jersey2892JacksonITCase extends AbstractJerseyEntityFilteringITCase {
+
+    @Override
+    protected String provider() {
+        return "jackson";
+    }
+}
diff --git a/tests/integration/jersey-780/pom.xml b/tests/integration/jersey-780/pom.xml
new file mode 100644
index 0000000..2962171
--- /dev/null
+++ b/tests/integration/jersey-780/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-780</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-780</name>
+
+    <description>Servlet integration test - JERSEY-780 - Malformed URL returns a 500 instead of a 400</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/jersey-780/src/main/java/org/glassfish/jersey/tests/integration/jersey780/HelloWorldResource.java b/tests/integration/jersey-780/src/main/java/org/glassfish/jersey/tests/integration/jersey780/HelloWorldResource.java
new file mode 100644
index 0000000..9049c36
--- /dev/null
+++ b/tests/integration/jersey-780/src/main/java/org/glassfish/jersey/tests/integration/jersey780/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey780;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/jersey-780/src/main/java/org/glassfish/jersey/tests/integration/jersey780/Jersey780.java b/tests/integration/jersey-780/src/main/java/org/glassfish/jersey/tests/integration/jersey780/Jersey780.java
new file mode 100644
index 0000000..5ea850a
--- /dev/null
+++ b/tests/integration/jersey-780/src/main/java/org/glassfish/jersey/tests/integration/jersey780/Jersey780.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey780;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Michal Gajdos
+ */
+public class Jersey780 extends Application {
+    @SuppressWarnings({"unchecked"})
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.<Class<?>>singleton(HelloWorldResource.class);
+    }
+}
diff --git a/tests/integration/jersey-780/src/main/webapp/WEB-INF/web.xml b/tests/integration/jersey-780/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..007d609
--- /dev/null
+++ b/tests/integration/jersey-780/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.jersey780.Jersey780</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/jersey-780/src/test/java/org/glassfish/jersey/tests/integration/jersey780/HelloWorldResourceITCase.java b/tests/integration/jersey-780/src/test/java/org/glassfish/jersey/tests/integration/jersey780/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..f54d531
--- /dev/null
+++ b/tests/integration/jersey-780/src/test/java/org/glassfish/jersey/tests/integration/jersey780/HelloWorldResourceITCase.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.jersey780;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig().registerInstances(new Jersey780());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInvalidUrl() throws Exception {
+        List<Integer> expectedCodes = Arrays.asList(
+                Response.Status.BAD_REQUEST.getStatusCode(), Response.Status.NOT_FOUND.getStatusCode());
+        List<String> expectedPhrases = Arrays.asList(
+                Response.Status.BAD_REQUEST.getReasonPhrase(), Response.Status.NOT_FOUND.getReasonPhrase());
+
+        final URL url = new URL(getBaseUri().toString() + "^");
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("GET");
+
+        connection.connect();
+
+        final int statusCode = connection.getResponseCode();
+        final String statusMessage = connection.getResponseMessage();
+
+        connection.disconnect();
+
+        assertTrue("Wrong response status code: " + statusCode,
+                expectedCodes.contains(statusCode));
+        assertTrue("Wrong response status reason: " + statusMessage,
+                expectedPhrases.contains(statusMessage));
+    }
+}
diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml
new file mode 100644
index 0000000..d28865c
--- /dev/null
+++ b/tests/integration/pom.xml
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.integration</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-integration</name>
+
+    <modules>
+        <module>async-jersey-filter</module>
+        <module>cdi-beanvalidation-webapp</module>
+        <module>cdi-ejb-test-webapp</module>
+        <module>cdi-iface-with-non-jaxrs-impl-test-webapp</module>
+        <module>cdi-multimodule</module>
+        <module>ejb-multimodule-reload</module>
+        <module>cdi-multipart-webapp</module>
+        <module>cdi-test-webapp</module>
+        <module>cdi-with-jersey-injection-custom-cfg-webapp</module>
+        <module>cdi-with-jersey-injection-custom-hk2-banned-webapp</module>
+        <module>cdi-with-jersey-injection-webapp</module>
+        <module>client-connector-provider</module>
+        <module>ejb-multimodule</module>
+        <module>ejb-test-webapp</module>
+        <module>j-376</module>
+        <module>j-441</module>
+        <module>j-59</module>
+        <module>jaxrs-component-inject</module>
+        <module>jersey-1107</module>
+        <module>jersey-1223</module>
+        <module>jersey-1604</module>
+        <module>jersey-1667</module>
+        <module>jersey-1829</module>
+        <module>jersey-1883</module>
+        <module>jersey-1928</module>
+        <module>jersey-1960</module>
+        <module>jersey-1964</module>
+        <module>jersey-2031</module>
+        <module>jersey-2136</module>
+        <module>jersey-2137</module>
+        <module>jersey-2154</module>
+        <module>jersey-2160</module>
+        <module>jersey-2164</module>
+        <module>jersey-2167</module>
+        <module>jersey-2176</module>
+        <module>jersey-2184</module>
+        <module>jersey-2255</module>
+        <module>jersey-2322</module>
+        <module>jersey-2335</module>
+        <module>jersey-2421</module>
+        <module>jersey-2551</module>
+        <module>jersey-2612</module>
+        <module>jersey-2637</module>
+        <module>jersey-2654</module>
+        <module>jersey-2673</module>
+        <module>jersey-2689</module>
+        <module>jersey-2704</module>
+        <module>jersey-2776</module>
+        <module>jersey-2794</module>
+        <module>jersey-2846</module>
+        <module>jersey-2878</module>
+        <module>jersey-2892</module>
+        <module>jersey-780</module>
+        <module>portability-jersey-1</module>
+        <module>portability-jersey-2</module>
+        <module>property-check</module>
+        <module>security-digest</module>
+        <module>servlet-2.5-autodiscovery-1</module>
+        <module>servlet-2.5-autodiscovery-2</module>
+        <module>servlet-2.5-filter</module>
+        <module>servlet-2.5-inflector-1</module>
+        <module>servlet-2.5-init-1</module>
+        <module>servlet-2.5-init-2</module>
+        <module>servlet-2.5-init-3</module>
+        <module>servlet-2.5-init-4</module>
+        <module>servlet-2.5-init-5</module>
+        <module>servlet-2.5-init-6</module>
+        <module>servlet-2.5-init-7</module>
+        <module>servlet-2.5-init-8</module>
+        <module>servlet-2.5-mvc-1</module>
+        <module>servlet-2.5-mvc-2</module>
+        <module>servlet-2.5-mvc-3</module>
+        <module>servlet-2.5-reload</module>
+        <module>servlet-3-async</module>
+        <module>servlet-3-chunked-io</module>
+        <module>servlet-3-filter</module>
+        <module>servlet-3-gf-async</module>
+        <module>servlet-3-inflector-1</module>
+        <module>servlet-3-init-1</module>
+        <module>servlet-3-init-2</module>
+        <module>servlet-3-init-3</module>
+        <module>servlet-3-init-4</module>
+        <module>servlet-3-init-5</module>
+        <module>servlet-3-init-6</module>
+        <module>servlet-3-init-7</module>
+        <module>servlet-3-init-8</module>
+        <module>servlet-3-init-provider</module>
+        <module>servlet-3-params</module>
+        <module>servlet-3-sse-1</module>
+        <module>servlet-request-wrapper-binding-2</module>
+        <module>servlet-request-wrapper-binding</module>
+        <module>servlet-tests</module>
+        <module>sonar-test</module>
+        <!-- TODO: temporarily removing spring 4 integration test -->
+        <!-- TODO: conflict in ASM version (too old) of jetty plugin -->
+        <!--<module>spring4</module>-->
+        <module>tracing-support</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>default</id>
+            <properties>
+                <env>default</env>
+                <jersey.config.test.container.port>9998</jersey.config.test.container.port>
+            </properties>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <properties>
+                <env>default</env>
+                <jersey.config.test.container.port>9998</jersey.config.test.container.port>
+                <jetty.log.file>${project.build.directory}/jetty-out.log</jetty.log.file>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <!-- configure Jetty to run in a separated JVM in order to get proper coverage -->
+                        <plugin>
+                            <groupId>org.mortbay.jetty</groupId>
+                            <artifactId>jetty-maven-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>start-jetty-forked</id>
+                                    <phase>pre-integration-test</phase>
+                                    <goals>
+                                        <goal>run-forked</goal>
+                                    </goals>
+                                    <configuration>
+                                        <contextPath>/</contextPath>
+                                        <scanIntervalSeconds>0</scanIntervalSeconds>
+                                        <waitForChild>false</waitForChild>
+                                        <jvmArgs>${server.coverage.argline} -Djetty.port=${jersey.config.test.container.port}
+                                            -Dorg.slf4j.simpleLogger.logFile=${jetty.log.file}
+                                        </jvmArgs>
+                                    </configuration>
+                                </execution>
+                                <execution>
+                                    <id>start-jetty</id>
+                                    <phase>none</phase>
+                                </execution>
+                            </executions>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.slf4j</groupId>
+                                    <artifactId>slf4j-simple</artifactId>
+                                    <version>1.7.5</version>
+                                </dependency>
+                            </dependencies>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+    </profiles>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.mortbay.jetty</groupId>
+                    <artifactId>jetty-maven-plugin</artifactId>
+                    <configuration>
+                        <skip>${skip.tests}</skip>
+                        <scanIntervalSeconds>10</scanIntervalSeconds>
+                        <stopPort>9999</stopPort>
+                        <stopKey>STOP</stopKey>
+                        <webApp>
+                            <contextPath>/</contextPath>
+                            <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+                        </webApp>
+                        <connectors>
+                            <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
+                                <port>${jersey.config.test.container.port}</port>
+                                <maxIdleTime>60000</maxIdleTime>
+                            </connector>
+                        </connectors>
+                        <jvmArgs>${server.coverage.argline}</jvmArgs>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <id>start-jetty</id>
+                            <phase>pre-integration-test</phase>
+                            <goals>
+                                <goal>run</goal>
+                            </goals>
+                            <configuration>
+                                <scanIntervalSeconds>0</scanIntervalSeconds>
+                                <daemon>true</daemon>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>stop-jetty</id>
+                            <phase>post-integration-test</phase>
+                            <goals>
+                                <goal>stop</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+</project>
diff --git a/tests/integration/portability-jersey-1/pom.xml b/tests/integration/portability-jersey-1/pom.xml
new file mode 100644
index 0000000..c8499d7
--- /dev/null
+++ b/tests/integration/portability-jersey-1/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>portability-jersey-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-portability-jersey-1</name>
+
+    <description>Servlet portability test - testing co-bundling with Jersey 1</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-servlet-portability</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-external</artifactId>
+            <version>${jersey1.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <version>${jersey1.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-servlet</artifactId>
+            <version>${jersey1.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <configuration>
+                  <classpathDependencyExcludes> <!-- this is to ensure only one Jersey version is present in the classpath -->
+                    <classpathDependencyExclude>org.glassfish.jersey.core:jersey-common</classpathDependencyExclude>
+                    <classpathDependencyExclude>org.glassfish.jersey.core:jersey-server</classpathDependencyExclude>
+                    <classpathDependencyExclude>org.glassfish.jersey.core:jersey-client</classpathDependencyExclude>
+                  </classpathDependencyExcludes>
+                </configuration>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/HelloWorldResource.java b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/HelloWorldResource.java
new file mode 100644
index 0000000..0f34d5b
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/HelloWorldResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Martin Matula
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World!";
+    }
+}
diff --git a/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Application.java b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Application.java
new file mode 100644
index 0000000..c56b9fa
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Application.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import com.sun.jersey.api.core.PackagesResourceConfig;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey1Application extends PackagesResourceConfig {
+    public Jersey1Application() {
+        // do the package scanning
+        super("org.glassfish.jersey.tests.integration.portability");
+
+        // explicitly add unannotated Jersey 1 specific resource
+        getExplicitRootResources().put("jersey", Jersey1Resource.class);
+    }
+}
diff --git a/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Resource.java b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Resource.java
new file mode 100644
index 0000000..7444fcd
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey1Resource {
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getJersey1() {
+        return "Using Jersey 1.x";
+    }
+}
diff --git a/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Application.java b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Application.java
new file mode 100644
index 0000000..91e0e1b
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Application.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey2Application extends ResourceConfig {
+
+    public Jersey2Application() {
+        // do the package scanning
+        packages("org.glassfish.jersey.tests.integration.portability");
+
+        // explicitly add unannotated Jersey 2 specific resource
+        Resource.Builder rb = Resource.builder(Jersey2Resource.class);
+        registerResources(rb.path("jersey").build());
+    }
+}
diff --git a/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Resource.java b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Resource.java
new file mode 100644
index 0000000..f7683e3
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey2Resource {
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getJersey2() {
+        return "Using Jersey 2.x";
+    }
+}
diff --git a/tests/integration/portability-jersey-1/src/main/webapp/WEB-INF/web.xml b/tests/integration/portability-jersey-1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5cd3e4c
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.portability.PortableServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey1#com.sun.jersey.config.property.resourceConfigClass</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.portability.Jersey1Application</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey2#javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.portability.Jersey2Application</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/portability-jersey-1/src/test/java/org/glassfish/jersey/tests/integration/portability/PortabilityITCase.java b/tests/integration/portability-jersey-1/src/test/java/org/glassfish/jersey/tests/integration/portability/PortabilityITCase.java
new file mode 100644
index 0000000..8d805e8
--- /dev/null
+++ b/tests/integration/portability-jersey-1/src/test/java/org/glassfish/jersey/tests/integration/portability/PortabilityITCase.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+import com.sun.jersey.test.framework.spi.container.TestContainerException;
+import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
+import com.sun.jersey.test.framework.spi.container.external.ExternalTestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class PortabilityITCase extends JerseyTest {
+
+    @Override
+    protected AppDescriptor configure() {
+        return new WebAppDescriptor.Builder().build();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = resource().path("helloworld").get(String.class);
+        assertEquals("Hello World!", s);
+    }
+
+    @Test
+    public void testJersey() {
+        ClientResponse r = resource().path("jersey").get(ClientResponse.class);
+        assertEquals(200, r.getStatus());
+        assertEquals("Using Jersey 1.x", r.getEntity(String.class));
+    }
+
+    /**
+     * The whole project is setup for Jersey 2. Need to get the effective port number
+     * from Jersey 2 properties to make Hudson happy.
+     *
+     * @param defaultPort to use if no other configuration is available
+     * @return port number to use by the client
+     */
+    @Override
+    protected int getPort(int defaultPort) {
+
+        String port = System.getProperty("jersey.config.test.container.port");
+        if (null != port) {
+            try {
+                return Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                throw new TestContainerException("jersey.config.test.container.port with a "
+                        + "value of \"" + port + "\" is not a valid integer.", e);
+            }
+        }
+
+        port = System.getProperty("JERSEY_TEST_PORT");
+        if (null != port) {
+            try {
+                return Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                throw new TestContainerException("JERSEY_TEST_PORT with a "
+                        + "value of \"" + port + "\" is not a valid integer.", e);
+            }
+        }
+        return defaultPort;
+    }
+}
diff --git a/tests/integration/portability-jersey-2/pom.xml b/tests/integration/portability-jersey-2/pom.xml
new file mode 100644
index 0000000..0e63542
--- /dev/null
+++ b/tests/integration/portability-jersey-2/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>portability-jersey-2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-portability-jersey-2</name>
+
+    <description>Servlet portability test - testing co-bundling with Jersey 2</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-servlet-portability</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-servlet</artifactId>
+            <version>${jersey1.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <configuration>
+                  <classpathDependencyExcludes> <!-- this is to ensure only one Jersey version is present in the classpath -->
+                    <classpathDependencyExclude>com.sun.jersey:jersey-core</classpathDependencyExclude>
+                    <classpathDependencyExclude>com.sun.jersey:jersey-server</classpathDependencyExclude>
+                    <classpathDependencyExclude>com.sun.jersey:jersey-servlet</classpathDependencyExclude>
+                  </classpathDependencyExcludes>
+                </configuration>
+             </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/HelloWorldResource.java b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/HelloWorldResource.java
new file mode 100644
index 0000000..0f34d5b
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/HelloWorldResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Martin Matula
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World!";
+    }
+}
diff --git a/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Application.java b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Application.java
new file mode 100644
index 0000000..c56b9fa
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Application.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import com.sun.jersey.api.core.PackagesResourceConfig;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey1Application extends PackagesResourceConfig {
+    public Jersey1Application() {
+        // do the package scanning
+        super("org.glassfish.jersey.tests.integration.portability");
+
+        // explicitly add unannotated Jersey 1 specific resource
+        getExplicitRootResources().put("jersey", Jersey1Resource.class);
+    }
+}
diff --git a/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Resource.java b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Resource.java
new file mode 100644
index 0000000..7444fcd
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey1Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey1Resource {
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getJersey1() {
+        return "Using Jersey 1.x";
+    }
+}
diff --git a/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Application.java b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Application.java
new file mode 100644
index 0000000..91e0e1b
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Application.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey2Application extends ResourceConfig {
+
+    public Jersey2Application() {
+        // do the package scanning
+        packages("org.glassfish.jersey.tests.integration.portability");
+
+        // explicitly add unannotated Jersey 2 specific resource
+        Resource.Builder rb = Resource.builder(Jersey2Resource.class);
+        registerResources(rb.path("jersey").build());
+    }
+}
diff --git a/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Resource.java b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Resource.java
new file mode 100644
index 0000000..f7683e3
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/main/java/org/glassfish/jersey/tests/integration/portability/Jersey2Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+public class Jersey2Resource {
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String getJersey2() {
+        return "Using Jersey 2.x";
+    }
+}
diff --git a/tests/integration/portability-jersey-2/src/main/webapp/WEB-INF/web.xml b/tests/integration/portability-jersey-2/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5cd3e4c
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.portability.PortableServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey1#com.sun.jersey.config.property.resourceConfigClass</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.portability.Jersey1Application</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey2#javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.portability.Jersey2Application</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>Jersey Web Application</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/portability-jersey-2/src/test/java/org/glassfish/jersey/tests/integration/portability/PortabilityITCase.java b/tests/integration/portability-jersey-2/src/test/java/org/glassfish/jersey/tests/integration/portability/PortabilityITCase.java
new file mode 100644
index 0000000..b158fda
--- /dev/null
+++ b/tests/integration/portability-jersey-2/src/test/java/org/glassfish/jersey/tests/integration/portability/PortabilityITCase.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.portability;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class PortabilityITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target("helloworld").request().get(String.class);
+        assertEquals("Hello World!", s);
+    }
+
+    @Test
+    public void testJersey() {
+        Response r = target("jersey").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("Using Jersey 2.x", r.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/property-check/pom.xml b/tests/integration/property-check/pom.xml
new file mode 100644
index 0000000..7fd7e4b
--- /dev/null
+++ b/tests/integration/property-check/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>property-check</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-integration-property-check</name>
+
+    <description>Property overlapping/duplicate check test</description>
+    <!--
+    Note that whenever new *Properties.java file is created, it has to be added in the test source file
+    in order to be checked for duplicates/overlapping.
+
+    Use find . -name "*Properties.java" in the Jersey root project folder to find files to add.
+    -->
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-common</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.security</groupId>
+            <artifactId>oauth1-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/property-check/src/test/java/org/glassfish/jersey/tests/integration/propertycheck/PropertyOverlappingCheckTest.java b/tests/integration/property-check/src/test/java/org/glassfish/jersey/tests/integration/propertycheck/PropertyOverlappingCheckTest.java
new file mode 100644
index 0000000..ef7072e
--- /dev/null
+++ b/tests/integration/property-check/src/test/java/org/glassfish/jersey/tests/integration/propertycheck/PropertyOverlappingCheckTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.propertycheck;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.apache.connector.ApacheClientProperties;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.internal.util.PropertiesClass;
+import org.glassfish.jersey.internal.util.Property;
+import org.glassfish.jersey.internal.util.PropertyAlias;
+import org.glassfish.jersey.jetty.connector.JettyClientProperties;
+import org.glassfish.jersey.media.multipart.MultiPartProperties;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.message.MessageProperties;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.internal.InternalServerProperties;
+import org.glassfish.jersey.server.oauth1.OAuth1ServerProperties;
+import org.glassfish.jersey.servlet.ServletProperties;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test, that there are no properties with overlapping names in known Jersey {@code *Properties} and {@code *Feature}
+ * classes.
+ * <p>
+ * For technical reasons, we do not want the property names to <i>overlap</i>.
+ * In other words, no property should contain a <i>namespace prefix</i>, that is already used as a concrete property name,
+ * such as {@code a.b} and {@code a.b.c}.
+ * </p>
+ * <p>
+ * Additionally, the test also reports all the duplicates property names found throughout the checked files.
+ * </p>
+ * <p>
+ * Note that the list of files is hardcoded directly in this test in a static array
+ * (to avoid the necessity of writing custom class loader for this test).
+ * If a java class containing properties should by included in the check, it has to be added here.
+ * Also note that the test is relying on {@link Property}, {@link PropertiesClass} and {@link PropertyAlias} annotations
+ * to recognize individual properties.
+ * </p>
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class PropertyOverlappingCheckTest {
+
+    private static final Logger log = Logger.getLogger(PropertyOverlappingCheckTest.class.getName());
+
+    private static final Class<?>[] classes = new Class[] {
+            JettyClientProperties.class,
+            ApacheClientProperties.class,
+            OAuth1ServerProperties.class,
+            ServletProperties.class,
+            CommonProperties.class,
+            MessageProperties.class,
+            ServerProperties.class,
+            InternalServerProperties.class,
+            ClientProperties.class,
+            MultiPartProperties.class,
+            TestProperties.class,
+            SseFeature.class
+    };
+
+    private static class ProblemReport {
+
+        private final String parentProperty;
+        private final String classNameParent;
+        private final String childProperty;
+        private final String classNameChild;
+        private final boolean duplicate;
+
+        private ProblemReport(String parentProperty,
+                              String classNameParent,
+                              String childProperty,
+                              String classNameChild,
+                              boolean duplicate) {
+            this.parentProperty = parentProperty;
+            this.classNameParent = classNameParent;
+            this.childProperty = childProperty;
+            this.classNameChild = classNameChild;
+            this.duplicate = duplicate;
+        }
+
+        private ProblemReport(String parentProperty, String classNameParent, String childProperty, String classNameChild) {
+            this(parentProperty, classNameParent, childProperty, classNameChild, false);
+        }
+
+        public String getParentProperty() {
+            return parentProperty;
+        }
+
+        public String getClassNameParent() {
+            return classNameParent;
+        }
+
+        public String getChildProperty() {
+            return childProperty;
+        }
+
+        public String getClassNameChild() {
+            return classNameChild;
+        }
+
+        public boolean isDuplicate() {
+            return duplicate;
+        }
+    }
+
+    @Test
+    public void test() throws IllegalAccessException {
+        List<String> allPropertyNames = new ArrayList<>();
+        Map<String, String> propertyToClassMap = new HashMap<>();
+        List<ProblemReport> problems = new ArrayList<>();
+
+        // iterate over all the string fields of above declared classes
+        for (Class<?> clazz : classes) {
+            final boolean checkFieldPropertyAnnotation = clazz.getAnnotation(PropertiesClass.class) == null;
+            Field[] fields = clazz.getFields();
+            for (Field field : fields) {
+                if (checkFieldPropertyAnnotation && field.getAnnotation(Property.class) == null) {
+                    // skip fields not annotated with @Property in classes not annotated with @PropertiesClass
+                    continue;
+                }
+                if (field.getAnnotation(PropertyAlias.class) != null) {
+                    // skip property aliases
+                    continue;
+                }
+                if (field.getType().isAssignableFrom(String.class)) {
+                    String propertyValue = (String) field.get(null);
+                    allPropertyNames.add(propertyValue);
+                    // check if there is already such property in the map; report a problem if true or store the
+                    // property-to-class relationship into the map for later use
+                    String propertyMapEntry = propertyToClassMap.get(propertyValue);
+                    if (propertyToClassMap.get(propertyValue) != null) {
+                        //                        log.info("Duplicate property found: " + propertyValue + " in " +
+                        // propertyMapEntry + " and "
+                        //                                + clazz.getName() + ". Test won't fail because of this, as the check
+                        // is currently disabled.");
+                        // this cannot cause the test to fail, as there are aliases in ClientProperties and ServerProperties,
+                        // which are by definition equal to those defined in CommonProperties
+                        problems.add(new ProblemReport(propertyValue, propertyMapEntry, propertyValue, clazz.getName(), true));
+                    } else {
+                        propertyToClassMap.put(propertyValue, clazz.getName());
+                    }
+                }
+            }
+        }
+        // sort the properties by name (natural), so that if two properties have overlapping names,
+        // they will appear one after another
+        Collections.sort(allPropertyNames);
+
+        String previousProperty = "";
+        for (String property : allPropertyNames) {
+            // is the property overlapping with the previous one?
+            // do not consider overlapping such as foo.bar vs foo.barbar, just foo.bar vs foo.bar.bar
+            if (property.startsWith(previousProperty + ".")) {
+                problems.add(new ProblemReport(previousProperty, propertyToClassMap.get(previousProperty),
+                        property, propertyToClassMap.get(property)));
+            } else {
+                // the "pointer" is moved only if there was no overlapping detected in this iteration
+                // as this would potentially hide the 2nd (or n-th) property overlapping with the same one
+                previousProperty = property;
+            }
+        }
+
+        if (!problems.isEmpty()) {
+            log.severe("Property naming problems detected: ");
+            for (ProblemReport problem : problems) {
+                if (problem.isDuplicate()) {
+                    log.severe("Duplicate property name: \n  property: " + problem.getParentProperty()
+                            + "\n  class1: " + problem.getClassNameParent()
+                            + "\n  class2: " + problem.getClassNameChild() + "\n");
+                } else {
+                    log.severe("Overlapping property names: \n  property1: "
+                            + problem.getParentProperty() + "\n  in: " + problem.getClassNameParent()
+                            + "\n  property2: "
+                            + problem.getChildProperty() + "\n  in " + problem.getClassNameChild() + "\n");
+                }
+            }
+        }
+        // fail if problems detected
+        assertTrue(problems.isEmpty());
+    }
+}
diff --git a/tests/integration/security-digest/pom.xml b/tests/integration/security-digest/pom.xml
new file mode 100644
index 0000000..4cffdf8
--- /dev/null
+++ b/tests/integration/security-digest/pom.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>security-digest</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-jersey-security-digest</name>
+    <url>http://maven.apache.org</url>
+
+
+    <description>Test of DIGEST authentication</description>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+
+                <configuration>
+                    <!-- define login service for jetty and the realm to be used -->
+                    <loginServices>
+                        <loginService implementation="org.eclipse.jetty.security.HashLoginService">
+                            <name>my-realm</name>
+                            <config>${basedir}/src/main/resources/jetty/realm.properties</config>
+                        </loginService>
+                    </loginServices>
+                </configuration>
+            </plugin>
+
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/MyApplication.java b/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/MyApplication.java
new file mode 100644
index 0000000..8c480aa
--- /dev/null
+++ b/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/MyApplication.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.securitydigest;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ *
+ * @author Miroslav Fuksa
+ */
+@ApplicationPath("/rest/*")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        register(MyResource.class);
+    }
+}
diff --git a/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/MyResource.java b/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/MyResource.java
new file mode 100644
index 0000000..7ebff65
--- /dev/null
+++ b/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/MyResource.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.securitydigest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.SecurityContext;
+
+import javax.inject.Inject;
+
+/**
+ * This resource contains methods that are secured using web.xml declarative security. Names of methods
+ * describe which user roles have access to them.
+ *
+ * @author Miroslav Fuksa
+ */
+@Path("resource")
+public class MyResource {
+    @Inject
+    SecurityContext securityContext;
+
+    @GET
+    public String getUserAdmin() {
+        return securityContext.getUserPrincipal().getName() + getAuth();
+    }
+
+    @POST
+    public String postUserAdmin(String entity) {
+        return "post-" + entity + "-" + securityContext.getUserPrincipal().getName() + getAuth();
+    }
+
+    @GET
+    @Path("sub")
+    public String getAdmin() {
+        return "subget-" + securityContext.getUserPrincipal().getName() + getAuth();
+    }
+
+    @Path("locator")
+    public Class<SubResource> getSubResourceUserAdmin() {
+        return SubResource.class;
+    }
+
+    private String getAuth() {
+        return "/scheme:" + securityContext.getAuthenticationScheme();
+    }
+
+}
diff --git a/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/SubResource.java b/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/SubResource.java
new file mode 100644
index 0000000..39c6edc
--- /dev/null
+++ b/tests/integration/security-digest/src/main/java/org/glassfish/jersey/tests/integration/securitydigest/SubResource.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.securitydigest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.SecurityContext;
+
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Miroslav Fuksa
+ */
+public class SubResource {
+    @Inject
+    SecurityContext securityContext;
+
+    @GET
+    public String getUserAdmin() {
+        return "locator-" + securityContext.getUserPrincipal().getName() + getAuth();
+    }
+
+    private String getAuth() {
+        return "/scheme:" + securityContext.getAuthenticationScheme();
+    }
+}
diff --git a/tests/integration/security-digest/src/main/resources/jetty/realm.properties b/tests/integration/security-digest/src/main/resources/jetty/realm.properties
new file mode 100644
index 0000000..3530df0
--- /dev/null
+++ b/tests/integration/security-digest/src/main/resources/jetty/realm.properties
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2013, 2018 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
+#
+
+homer:Homer,my-user
+marge:Marge,my-admin
+bart:Bart,my-user,my-admin
diff --git a/tests/integration/security-digest/src/main/webapp/WEB-INF/web.xml b/tests/integration/security-digest/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..b704f48
--- /dev/null
+++ b/tests/integration/security-digest/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <display-name>Archetype Created Web Application</display-name>
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.securitydigest.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.securitydigest.MyApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.securitydigest.MyApplication</servlet-name>
+        <url-pattern>/rest/*</url-pattern>
+    </servlet-mapping>
+    <welcome-file-list>
+        <welcome-file>index.html</welcome-file>
+    </welcome-file-list>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>rest api</web-resource-name>
+            <url-pattern>/rest/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>my-user</role-name>
+            <role-name>my-admin</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>rest api</web-resource-name>
+            <url-pattern>/rest/resource/sub</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>my-admin</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>my-user</role-name>
+    </security-role>
+    <security-role>
+        <role-name>my-admin</role-name>
+    </security-role>
+
+
+    <login-config>
+        <auth-method>DIGEST</auth-method>
+        <realm-name>my-realm</realm-name>
+    </login-config>
+
+</web-app>
diff --git a/tests/integration/security-digest/src/test/java/org/glassfish/jersey/tests/integration/securitydigest/SecurityDigestAuthenticationITCase.java b/tests/integration/security-digest/src/test/java/org/glassfish/jersey/tests/integration/securitydigest/SecurityDigestAuthenticationITCase.java
new file mode 100644
index 0000000..22b422c
--- /dev/null
+++ b/tests/integration/security-digest/src/test/java/org/glassfish/jersey/tests/integration/securitydigest/SecurityDigestAuthenticationITCase.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.securitydigest;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Miroslav Fuksa
+ */
+public class SecurityDigestAuthenticationITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(MyApplication.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(new LoggingFeature(Logger.getLogger(SecurityDigestAuthenticationITCase.class.getName()),
+                LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    @Test
+    public void testResourceGet() {
+        _testResourceGet(HttpAuthenticationFeature.digest("homer", "Homer"));
+        _testResourceGet(HttpAuthenticationFeature.universal("homer", "Homer"));
+        _testResourceGet(HttpAuthenticationFeature.universalBuilder().credentialsForDigest("homer", "Homer").build());
+        _testResourceGet(HttpAuthenticationFeature.universalBuilder().credentialsForDigest("homer", "Homer")
+                .credentialsForBasic("aaa", "bbb").build());
+    }
+
+    public void _testResourceGet(HttpAuthenticationFeature feature) {
+        final Response response = target().path("rest/resource")
+                .register(feature).request().get();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("homer/scheme:DIGEST", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceGet401() {
+        _testResourceGet401(HttpAuthenticationFeature.digest("nonexisting", "foo"));
+        _testResourceGet401(HttpAuthenticationFeature.universalBuilder().credentials("nonexisting", "foo").build());
+    }
+
+    public void _testResourceGet401(HttpAuthenticationFeature feature) {
+        final Response response = target().path("rest/resource")
+                .register(feature).request().get();
+
+        Assert.assertEquals(401, response.getStatus());
+    }
+
+    @Test
+    public void testResourcePost() {
+        _testResourcePost(HttpAuthenticationFeature.digest("homer", "Homer"));
+        _testResourcePost(HttpAuthenticationFeature.universal("homer", "Homer"));
+    }
+
+    public void _testResourcePost(HttpAuthenticationFeature feature) {
+        final Response response = target().path("rest/resource")
+                .register(feature).request()
+                .post(Entity.entity("helloworld", MediaType.TEXT_PLAIN_TYPE));
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("post-helloworld-homer/scheme:DIGEST", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceSubGet403() {
+        _testResourceSubGet403(HttpAuthenticationFeature.digest("homer", "Homer"));
+        _testResourceSubGet403(HttpAuthenticationFeature.universal("homer", "Homer"));
+    }
+
+    public void _testResourceSubGet403(HttpAuthenticationFeature feature) {
+        final Response response = target().path("rest/resource/sub")
+                .register(feature).request().get();
+
+        Assert.assertEquals(403, response.getStatus());
+    }
+
+    @Test
+    public void testResourceSubGet() {
+        _testResourceSubGet2(HttpAuthenticationFeature.digest("bart", "Bart"));
+        _testResourceSubGet2(HttpAuthenticationFeature.universal("bart", "Bart"));
+    }
+
+    public void _testResourceSubGet2(HttpAuthenticationFeature feature) {
+        final Response response = target().path("rest/resource/sub")
+                .register(feature).request().get();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("subget-bart/scheme:DIGEST", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceLocatorGet() {
+        _testResourceLocatorGet(HttpAuthenticationFeature.digest("bart", "Bart"));
+        _testResourceLocatorGet(HttpAuthenticationFeature.universal("bart", "Bart"));
+    }
+
+    public void _testResourceLocatorGet(HttpAuthenticationFeature feature) {
+
+        final Response response = target().path("rest/resource/locator")
+                .register(feature).request().get();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("locator-bart/scheme:DIGEST", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceMultipleRequestsWithOneFilter() {
+        _testResourceMultipleRequestsWithOneFilter(HttpAuthenticationFeature.digest("homer", "Homer"));
+        _testResourceMultipleRequestsWithOneFilter(HttpAuthenticationFeature.universal("homer", "Homer"));
+    }
+
+    public void _testResourceMultipleRequestsWithOneFilter(HttpAuthenticationFeature haf) {
+        WebTarget target = target().path("rest/resource")
+                .register(haf);
+        Response response = target.request().get();
+
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("homer/scheme:DIGEST", response.readEntity(String.class));
+
+        response = target.request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("homer/scheme:DIGEST", response.readEntity(String.class));
+
+        response = target.path("sub").request().get();
+        Assert.assertEquals(403, response.getStatus());
+
+        response = target.request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("homer/scheme:DIGEST", response.readEntity(String.class));
+
+        response = target.path("locator").request().get();
+        Assert.assertEquals(200, response.getStatus());
+        Assert.assertEquals("locator-homer/scheme:DIGEST", response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/integration/servlet-2.5-autodiscovery-1/pom.xml b/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
new file mode 100644
index 0000000..65d81ff
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-autodiscovery-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-autodiscovery-1</name>
+
+    <description>Servlet integration test - servlet-2.5-autodiscovery-1 - auto-discovery of UriConnegFilter</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-autodiscovery-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_1/LanguageVariantResource.java b/tests/integration/servlet-2.5-autodiscovery-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_1/LanguageVariantResource.java
new file mode 100644
index 0000000..cc161c9
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_1/LanguageVariantResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_autodiscovery_1;
+
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Variant;
+
+/**
+ * @author Martin Matula
+ * @author Michal Gajdos
+ */
+@Path("/abc")
+public class LanguageVariantResource {
+
+    @GET
+    public Response doGet(@Context Request r) {
+        List<Variant> vs = Variant.VariantListBuilder.newInstance()
+                .mediaTypes(MediaType.valueOf("application/foo"))
+                .languages(new Locale("en")).languages(new Locale("fr")).add()
+                .mediaTypes(MediaType.valueOf("application/bar"))
+                .languages(new Locale("en")).languages(new Locale("fr")).add()
+                .build();
+
+        Variant v = r.selectVariant(vs);
+        if (v == null) {
+            return Response.notAcceptable(vs).build();
+        } else {
+            return Response.ok(v.getMediaType().toString() + ", " + v.getLanguage(), v).build();
+        }
+    }
+}
diff --git a/tests/integration/servlet-2.5-autodiscovery-1/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-autodiscovery-1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d45de83
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testAutoDiscovery1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_autodiscovery_1</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.mediaTypeMappings</param-name>
+            <param-value>foo : application/foo, bar : application/bar</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.languageMappings</param-name>
+            <param-value>english : en, french : fr</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testAutoDiscovery1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-autodiscovery-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_1/LanguageVariantResourceITCase.java b/tests/integration/servlet-2.5-autodiscovery-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_1/LanguageVariantResourceITCase.java
new file mode 100644
index 0000000..af45d99
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_1/LanguageVariantResourceITCase.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_autodiscovery_1;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class LanguageVariantResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(LanguageVariantResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testMediaTypesAndLanguages() {
+        _test("english", "foo", "en", "application/foo");
+        _test("french", "foo", "fr", "application/foo");
+
+        _test("english", "bar", "en", "application/bar");
+        _test("french", "bar", "fr", "application/bar");
+    }
+
+    private void _test(String ul, String um, String l, String m) {
+        Response r = target().path("abc." + ul + "." + um).request().get();
+        assertEquals(m + ", " + l, r.readEntity(String.class));
+        assertEquals(l, r.getLanguage().toString());
+        assertEquals(m, r.getMediaType().toString());
+
+        r = target().path("abc." + um + "." + ul).request().get();
+        assertEquals(m + ", " + l, r.readEntity(String.class));
+        assertEquals(l, r.getLanguage().toString());
+        assertEquals(m, r.getMediaType().toString());
+
+        r = target().path("abc").request(m).header(HttpHeaders.ACCEPT_LANGUAGE, l).get();
+        assertEquals(m + ", " + l, r.readEntity(String.class));
+        assertEquals(l, r.getLanguage().toString());
+        assertEquals(m, r.getMediaType().toString());
+    }
+}
diff --git a/tests/integration/servlet-2.5-autodiscovery-2/pom.xml b/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
new file mode 100644
index 0000000..cc220e3
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-autodiscovery-2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-autodiscovery-2</name>
+
+    <description>Servlet integration test - servlet-2.5-autodiscovery-2 - auto-discovery of Bean Validation (JAX-RS bundle)</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.bundles</groupId>
+            <artifactId>jaxrs-ri</artifactId>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-autodiscovery-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_2/ValidationResource.java b/tests/integration/servlet-2.5-autodiscovery-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_2/ValidationResource.java
new file mode 100644
index 0000000..50158e8
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_2/ValidationResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_autodiscovery_2;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/")
+public class ValidationResource {
+
+    @POST
+    @NotNull
+    public String post(@NotNull @Size(min = 1) final String value) {
+        return value.isEmpty() || "|".equals(value) ? null : value;
+    }
+}
diff --git a/tests/integration/servlet-2.5-autodiscovery-2/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-autodiscovery-2/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..fbadfc6
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testAutoDiscovery2</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_autodiscovery_2</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testAutoDiscovery2</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-autodiscovery-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_2/ValidationResourceITCase.java b/tests/integration/servlet-2.5-autodiscovery-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_2/ValidationResourceITCase.java
new file mode 100644
index 0000000..646aeda
--- /dev/null
+++ b/tests/integration/servlet-2.5-autodiscovery-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_autodiscovery_2/ValidationResourceITCase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_autodiscovery_2;
+
+import javax.ws.rs.client.Entity;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class ValidationResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(ValidationResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testValidationPositive() {
+        assertEquals("value", target().request().post(Entity.text("value")).readEntity(String.class));
+    }
+
+    @Test
+    public void testValidationRequestNegative() {
+        assertEquals(500, target().request().post(Entity.text("|")).getStatus());
+    }
+
+    @Test
+    public void testValidatioResponsenNegative() {
+        assertEquals(400, target().request().post(null).getStatus());
+    }
+}
diff --git a/tests/integration/servlet-2.5-filter/pom.xml b/tests/integration/servlet-2.5-filter/pom.xml
new file mode 100644
index 0000000..60416db
--- /dev/null
+++ b/tests/integration/servlet-2.5-filter/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-filter</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-filter</name>
+
+    <description>Servlet integration test - servlet-2.5-filter</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-2.5-filter/src/main/java/org/glassfish/jersey/tests/integration/servlet_2_5_filter/MyResource.java b/tests/integration/servlet-2.5-filter/src/main/java/org/glassfish/jersey/tests/integration/servlet_2_5_filter/MyResource.java
new file mode 100644
index 0000000..3f0a802
--- /dev/null
+++ b/tests/integration/servlet-2.5-filter/src/main/java/org/glassfish/jersey/tests/integration/servlet_2_5_filter/MyResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_2_5_filter;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("myresource")
+public class MyResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "OK";
+    }
+}
diff --git a/tests/integration/servlet-2.5-filter/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-filter/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..4b8aefa
--- /dev/null
+++ b/tests/integration/servlet-2.5-filter/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>Jersey Web Application</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_2_5_filter</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.filter.contextPath</param-name>
+            <param-value>/myapp/</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>Jersey Web Application</filter-name>
+        <url-pattern>/myapp/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-filter/src/test/java/org/glassfish/jersey/tests/integration/servlet_2_5_filter/MyResourceITCase.java b/tests/integration/servlet-2.5-filter/src/test/java/org/glassfish/jersey/tests/integration/servlet_2_5_filter/MyResourceITCase.java
new file mode 100644
index 0000000..824a01c
--- /dev/null
+++ b/tests/integration/servlet-2.5-filter/src/test/java/org/glassfish/jersey/tests/integration/servlet_2_5_filter/MyResourceITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_2_5_filter;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class MyResourceITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig rc = new ResourceConfig();
+        return rc.packages(MyResourceITCase.class.getPackage().getName());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testMyResource() throws Exception {
+        final Response response = target().path("myapp").path("myresource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testWadl() {
+        final Response response = target().path("myapp/application.wadl").request().get();
+        assertEquals(200, response.getStatus());
+        assertTrue(response.readEntity(String.class).startsWith("<?xml version=\"1.0\""));
+    }
+}
diff --git a/tests/integration/servlet-2.5-inflector-1/pom.xml b/tests/integration/servlet-2.5-inflector-1/pom.xml
new file mode 100644
index 0000000..a9f968c
--- /dev/null
+++ b/tests/integration/servlet-2.5-inflector-1/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-inflector-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-inflector-1</name>
+
+    <description>
+        Servlet integration test - servlet-2.5-inflector-1 - Check that inflectors in programmatically created resources are
+        properly injected.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-2.5-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/MyApplication.java b/tests/integration/servlet-2.5-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/MyApplication.java
new file mode 100644
index 0000000..ec9d5ec
--- /dev/null
+++ b/tests/integration/servlet-2.5-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/MyApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_inflector_1;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        final Resource.Builder resourceBuilder = Resource.builder("resource");
+
+        resourceBuilder.addChildResource("class")
+                .addMethod("GET")
+                .handledBy(MyInflector.class);
+        resourceBuilder.addChildResource("instance")
+                .addMethod("GET")
+                .handledBy(new MyInflector());
+
+        registerResources(resourceBuilder.build());
+    }
+}
diff --git a/tests/integration/servlet-2.5-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/MyInflector.java b/tests/integration/servlet-2.5-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/MyInflector.java
new file mode 100644
index 0000000..84c9a3b
--- /dev/null
+++ b/tests/integration/servlet-2.5-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/MyInflector.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_inflector_1;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.glassfish.jersey.process.Inflector;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyInflector implements Inflector<ContainerRequestContext, Response> {
+
+    @Inject
+    private Provider<HttpServletRequest> requestProvider;
+    @Inject
+    private Provider<HttpServletResponse> responseProvider;
+
+    @Override
+    public Response apply(final ContainerRequestContext requestContext) {
+        final StringBuilder stringBuilder = new StringBuilder();
+
+        // Request provider & request.
+        if (requestProvider != null) {
+            stringBuilder.append("requestProvider_");
+            stringBuilder.append(requestProvider.get() != null ? "request" : null);
+        } else {
+            stringBuilder.append("null_null");
+        }
+
+        stringBuilder.append('_');
+
+        // Response provider & response.
+        if (responseProvider != null) {
+            stringBuilder.append("responseProvider_");
+            stringBuilder.append(responseProvider.get() != null ? "response" : null);
+        } else {
+            stringBuilder.append("null_null");
+        }
+
+        return Response.ok(stringBuilder.toString()).build();
+    }
+}
diff --git a/tests/integration/servlet-2.5-inflector-1/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-inflector-1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..2806fe6
--- /dev/null
+++ b/tests/integration/servlet-2.5-inflector-1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_25_inflector_1.MyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_inflector_1.MyApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_25_inflector_1.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-inflector-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/InflectorInjectionTestITCase.java b/tests/integration/servlet-2.5-inflector-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/InflectorInjectionTestITCase.java
new file mode 100644
index 0000000..9ccb2cb
--- /dev/null
+++ b/tests/integration/servlet-2.5-inflector-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_inflector_1/InflectorInjectionTestITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_inflector_1;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class InflectorInjectionTestITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInflectorClassInjection() throws Exception {
+        _testInflectorInjection("class");
+    }
+
+    @Test
+    public void testInflectorInstanceInjection() throws Exception {
+        _testInflectorInjection("instance");
+    }
+
+    private void _testInflectorInjection(final String path) {
+        final Response response = target("resource").path(path).request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("requestProvider_request_responseProvider_response", response.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-1/pom.xml b/tests/integration/servlet-2.5-init-1/pom.xml
new file mode 100644
index 0000000..71dd7e5
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-1</name>
+
+    <description>Servlet integration test - servlet-2.5-init-1 - configured via jax-rs application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/ClientUsingResource.java b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/ClientUsingResource.java
new file mode 100644
index 0000000..0728579
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/ClientUsingResource.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_init_1;
+
+import java.net.URI;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * A resource that instantiated & uses JAX-RS/Jersey client to access another resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("viaclient")
+public class ClientUsingResource {
+    @Path("helloworld")
+    @GET
+    public String getProxiedMessage(@Context UriInfo uriInfo) {
+        return ClientBuilder.newClient()
+                .target(uriInfo.resolve(URI.create("helloworld")))
+                .request("text/plain")
+                .get(String.class);
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/HelloWorldResource.java b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/HelloWorldResource.java
new file mode 100644
index 0000000..2ab6f5b
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/HelloWorldResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.glassfish.jersey.servlet.WebConfig;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Martin Matula
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+
+    @GET
+    @Path("injection")
+    public String getInjection(@Context HttpServletRequest request, @Context HttpServletResponse response,
+                               @Context WebConfig webConfig, @Context ServletConfig servletConfig,
+                               @Context ServletContext servletContext) {
+        return request.getMethod() + (response != null) + webConfig.getName() + servletConfig.getServletName()
+                + servletContext.getServletContextName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/MultipleLinksResource.java b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/MultipleLinksResource.java
new file mode 100644
index 0000000..adcafea
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/MultipleLinksResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_init_1;
+
+import java.net.URI;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Reproducer for JERSEY-1801. See also E2E {@code LinkTest}.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("links")
+public class MultipleLinksResource {
+
+    @GET
+    public Response get(@Context UriInfo uriInfo) throws Exception {
+        URI test1 = URI.create(uriInfo.getAbsolutePath().toString() + "test1");
+        URI test2 = URI.create(uriInfo.getAbsolutePath().toString() + "test2");
+
+        return Response.ok()
+                .link("http://oracle.com", "parent")
+                .link(new URI("http://jersey.java.net"), "framework")
+                .links(
+                        Link.fromUri(uriInfo.relativize(test1)).rel("test1").build(),
+                        Link.fromUri(test2).rel("test2").build(),
+                        Link.fromUri(uriInfo.relativize(URI.create("links/test3"))).rel("test3").build()
+                ).build();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25init1.java b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25init1.java
new file mode 100644
index 0000000..b6aec66
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25init1.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_1;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * JAX-RS application for the Servlet 2.5 initialization test #01.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@ApplicationPath("application_path")
+public class Servlet25init1 extends Application {
+    @SuppressWarnings({"unchecked"})
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<>(
+                Arrays.asList(HelloWorldResource.class, MultipleLinksResource.class, ClientUsingResource.class));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/UnreachableResource.java b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/UnreachableResource.java
new file mode 100644
index 0000000..42e042a
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/UnreachableResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ *
+ * @author Martin Matula
+ */
+@Path("unreachable")
+public class UnreachableResource {
+    @GET
+    public Response get() {
+        return Response.ok().build();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-1/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d5a0798
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_1.Servlet25init1</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/servlet_path/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java b/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java
new file mode 100644
index 0000000..7ee6968
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_1/Servlet25Init1ITCase.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_init_1;
+
+import java.net.URI;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.uri.UriTemplate;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Servlet 2.5 initialization test #01.
+ *
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Martin Matula
+ */
+public class Servlet25Init1ITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Servlet25init1();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("servlet_path/helloworld").request().get(String.class);
+        assertEquals("Hello World! " + this.getClass().getPackage().getName(), s);
+    }
+
+    @Test
+    public void testHelloWorldAtWrongPath() {
+        Response r = target().path("application_path/helloworld").request().get();
+        assertTrue(
+                "Request to application_path/helloworld should have failed, but did not. That means two applications are "
+                        + "registered.",
+                r.getStatus() >= 400);
+    }
+
+    @Test
+    public void testHelloWorldViaClientInResource() throws Exception {
+        String s = target().path("servlet_path/viaclient/helloworld").request().get(String.class);
+        assertEquals("Hello World! " + this.getClass().getPackage().getName(), s);
+    }
+
+    @Test
+    public void testUnreachableResource() {
+        Response r = target().path("servlet_path/unreachable").request().get();
+        assertTrue("Managed to reach a resource that is not registered in the application.", r.getStatus() >= 400);
+    }
+
+    @Test
+    public void testUnreachableResourceAtWrongPath() {
+        Response r = target().path("application_path/unreachable").request().get();
+        assertTrue("Managed to reach a resource that is not registered in the application.", r.getStatus() >= 400);
+    }
+
+    @Test
+    public void testInjection() {
+        String s = target().path("servlet_path/helloworld/injection").request().get(String.class);
+        assertEquals("GETtruetestServlet1testServlet1/", s);
+    }
+
+    // Reproducer for JERSEY-1801
+    @Test
+    public void multipleLinksTest() {
+        final WebTarget target = target("/servlet_path/links/");
+        final Response response = target.request().get();
+        assertThat(response.getStatus(), equalTo(200));
+
+        final URI targetUri = target.getUri();
+        assertThat(response.getLink("parent").getUri(), equalTo(URI.create("http://oracle.com")));
+        assertThat(response.getLink("framework").getUri(), equalTo(URI.create("http://jersey.java.net")));
+
+        assertThat(response.getLink("test1").getUri(), equalTo(UriTemplate.resolve(targetUri, "test1")));
+        assertThat(response.getLink("test2").getUri(), equalTo(UriTemplate.resolve(targetUri, "test2")));
+        assertThat(response.getLink("test3").getUri(), equalTo(UriTemplate.resolve(targetUri, "test3")));
+    }
+
+}
diff --git a/tests/integration/servlet-2.5-init-2/pom.xml b/tests/integration/servlet-2.5-init-2/pom.xml
new file mode 100644
index 0000000..b069eff
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-2</name>
+
+    <description>Servlet integration test - servlet-2.5-init-2 - configured using package scanning</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/CustomFeature.java b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/CustomFeature.java
new file mode 100644
index 0000000..1f99758
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/CustomFeature.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.tests.integration.servlet_25_init_2.ext.Ext1WriterInterceptor;
+import org.glassfish.jersey.tests.integration.servlet_25_init_2.ext.Ext2WriterInterceptor;
+import org.glassfish.jersey.tests.integration.servlet_25_init_2.ext.Ext3WriterInterceptor;
+import org.glassfish.jersey.tests.integration.servlet_25_init_2.ext.Ext4WriterInterceptor;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class CustomFeature implements Feature {
+
+    @Override
+    public boolean configure(final FeatureContext context) {
+        context.register(Ext3WriterInterceptor.class, 1000);
+        context.register(Ext2WriterInterceptor.class, 100);
+        context.register(Ext1WriterInterceptor.INSTANCE, 500);
+        context.register(Ext4WriterInterceptor.INSTANCE, 1);
+        return true;
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/HelloWorldResource.java b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/HelloWorldResource.java
new file mode 100644
index 0000000..3867cd2
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext1WriterInterceptor.java b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext1WriterInterceptor.java
new file mode 100644
index 0000000..1ff8e53
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext1WriterInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext1WriterInterceptor implements WriterInterceptor {
+
+    public static final Ext1WriterInterceptor INSTANCE = new Ext1WriterInterceptor();
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext1");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext2WriterInterceptor.java b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext2WriterInterceptor.java
new file mode 100644
index 0000000..80eebfe
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext2WriterInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext2WriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext2");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext3WriterInterceptor.java b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext3WriterInterceptor.java
new file mode 100644
index 0000000..885f151
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext3WriterInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext3WriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext3");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext4WriterInterceptor.java b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext4WriterInterceptor.java
new file mode 100644
index 0000000..d90988a
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/ext/Ext4WriterInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext4WriterInterceptor implements WriterInterceptor {
+
+    public static final Ext4WriterInterceptor INSTANCE = new Ext4WriterInterceptor();
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext4");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-2/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-2/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..7066c4b
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testServlet2</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_2</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.provider.scanning.recursive</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet2</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..89071b3
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_2/HelloWorldResourceITCase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_2;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        Response r = target().path("helloworld").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("Hello World! " + this.getClass().getPackage().getName() + "-ext4-ext2-ext1-ext3",
+                r.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-3/pom.xml b/tests/integration/servlet-2.5-init-3/pom.xml
new file mode 100644
index 0000000..2271b3d
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-3/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-3</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-3</name>
+
+    <description>Servlet integration test - servlet-2.5-init-3 - configured by explicitly set resource classes</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_3/HelloWorldResource.java b/tests/integration/servlet-2.5-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_3/HelloWorldResource.java
new file mode 100644
index 0000000..3df9bbe
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_3/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_3;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-3/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-3/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ad4c999
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-3/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testServlet3</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_3.HelloWorldResource</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet3</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_3/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_3/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..ac2c4fe
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_3/HelloWorldResourceITCase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_3;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-4/pom.xml b/tests/integration/servlet-2.5-init-4/pom.xml
new file mode 100644
index 0000000..2b119f0
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-4/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-4</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-4</name>
+
+    <description>Servlet integration test - servlet-2.5-init-4 - configured by explicitly set resource and provider class name</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWorldResource.java b/tests/integration/servlet-2.5-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWorldResource.java
new file mode 100644
index 0000000..ebf7b5b
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWorldResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_4;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public Hello get() {
+        return new Hello();
+    }
+
+    public static class Hello {
+
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWriter.java b/tests/integration/servlet-2.5-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWriter.java
new file mode 100644
index 0000000..54113d1
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWriter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_4;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Provider
+public class HelloWriter implements MessageBodyWriter<HelloWorldResource.Hello> {
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(HelloWorldResource.Hello.class);
+    }
+
+    @Override
+    public long getSize(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
+            throws IOException, WebApplicationException {
+        entityStream.write(("Hello World! " + this.getClass().getPackage().getName())
+                .getBytes(MessageUtils.getCharset(mediaType)));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-4/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-4/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..b3b168e
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-4/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>testServlet4</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_4.HelloWorldResource;org.glassfish.jersey.tests.integration.servlet_25_init_4.HelloWriter</param-value>
+        </init-param>
+
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet4</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-4/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-4/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..c16b5ff
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-4/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_4/HelloWorldResourceITCase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_4;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-5/pom.xml b/tests/integration/servlet-2.5-init-5/pom.xml
new file mode 100644
index 0000000..bce35f3
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-5/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-5</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-5</name>
+
+    <description>Servlet integration test - servlet-2.5-init-5 - filter with specified jax rs application class</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java
new file mode 100644
index 0000000..16003d3
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_5;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("filter_path/helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/Servlet25init5.java b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/Servlet25init5.java
new file mode 100644
index 0000000..272e070
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/Servlet25init5.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_5;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@ApplicationPath("application_path")
+public class Servlet25init5 extends Application{
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return Collections.singleton(HelloWorldResource.class);
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java
new file mode 100644
index 0000000..24eee33
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_5;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ *
+ * @author Martin Matula
+ */
+@Path("filter_path/unreachable")
+public class UnreachableResource {
+    @GET
+    public Response get() {
+        return Response.ok().build();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-5/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-5/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..271170f
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-5/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>testServlet5</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_5.Servlet25init5</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testServlet5</filter-name>
+        <url-pattern>/filter_path/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..561412a
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_5;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("filter_path/helloworld").request().get(String.class);
+        assertEquals("Hello World! " + this.getClass().getPackage().getName(), s);
+    }
+
+    @Test
+    public void testHelloWorldAtWrongPath() {
+        Response r = target().path("application_path/filter_path/helloworld").request().get();
+        assertTrue(
+                "Request to application_path/helloworld should have failed, but did not. That means two applications are "
+                        + "registered.",
+                r.getStatus() >= 400);
+    }
+
+    @Test
+    @Ignore
+    public void testUnreachableResource() {
+        Response r = target().path("filter_path/unreachable").request().get();
+        assertTrue("Managed to reach a resource that is not registered in the application.", r.getStatus() >= 400);
+    }
+
+    @Test
+    public void testUnreachableResourceAtWrongPath() {
+        Response r = target().path("application_path/filter_path/unreachable").request().get();
+        assertTrue("Managed to reach a resource that is not registered in the application.", r.getStatus() >= 400);
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-6/pom.xml b/tests/integration/servlet-2.5-init-6/pom.xml
new file mode 100644
index 0000000..aa89759
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-6/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-6</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-6</name>
+
+    <description>Servlet integration test - servlet-2.5-init-6 - filter - configured using package scanning</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_6/HelloWorldResource.java b/tests/integration/servlet-2.5-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_6/HelloWorldResource.java
new file mode 100644
index 0000000..0d5c6e3
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_6/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_6;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-6/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-6/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..df4e84f
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-6/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>testServlet6</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_6</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testServlet6</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-6/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_6/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-6/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_6/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..e10063f
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-6/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_6/HelloWorldResourceITCase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_6;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-7/pom.xml b/tests/integration/servlet-2.5-init-7/pom.xml
new file mode 100644
index 0000000..231efe9
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-7/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-7</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-7</name>
+
+    <description>Servlet integration test - servlet-2.5-init-7 - filter - configured by explicitly set resource classes</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_7/HelloWorldResource.java b/tests/integration/servlet-2.5-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_7/HelloWorldResource.java
new file mode 100644
index 0000000..3b924d4
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_7/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_7;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-7/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-7/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..859d505
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-7/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>testServlet7</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_7.HelloWorldResource</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testServlet7</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-7/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_7/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-7/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_7/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..b1edf72
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-7/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_7/HelloWorldResourceITCase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_7;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-8/pom.xml b/tests/integration/servlet-2.5-init-8/pom.xml
new file mode 100644
index 0000000..bb916ce
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-8/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-init-8</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-init-8</name>
+
+    <description>Servlet integration test - servlet-2.5-init-8 - filter - configured by explicitly set resource and provider class name</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWorldResource.java b/tests/integration/servlet-2.5-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWorldResource.java
new file mode 100644
index 0000000..ab3917d
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWorldResource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_8;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public Hello get() {
+        return new Hello();
+    }
+
+    public static class Hello {
+
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWriter.java b/tests/integration/servlet-2.5-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWriter.java
new file mode 100644
index 0000000..62dcb6f
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWriter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_8;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Provider
+public class HelloWriter implements MessageBodyWriter<HelloWorldResource.Hello> {
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(HelloWorldResource.Hello.class);
+    }
+
+    @Override
+    public long getSize(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
+            throws IOException, WebApplicationException {
+        entityStream.write(("Hello World! " + this.getClass().getPackage().getName())
+                .getBytes(MessageUtils.getCharset(mediaType)));
+    }
+}
diff --git a/tests/integration/servlet-2.5-init-8/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-init-8/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1aaa6d3
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-8/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>testServlet8</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_init_8.HelloWorldResource;org.glassfish.jersey.tests.integration.servlet_25_init_8.HelloWriter</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testServlet8</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..ba304ba
--- /dev/null
+++ b/tests/integration/servlet-2.5-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_8/HelloWorldResourceITCase.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_init_8;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/pom.xml b/tests/integration/servlet-2.5-mvc-1/pom.xml
new file mode 100644
index 0000000..4423806
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-mvc-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-mvc-1</name>
+
+    <description>Servlet integration test - servlet-2.5-mvc-1 - filter - MVC</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-jsp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/MyApplication.java b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/MyApplication.java
new file mode 100644
index 0000000..973f3ce
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/MyApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_1.resource.Bookstore;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        // Resources.
+        packages(Bookstore.class.getPackage().getName());
+
+        // MVC.
+        register(JspMvcFeature.class);
+
+        // Logging.
+        register(LoggingFeature.class);
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Book.java b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Book.java
new file mode 100644
index 0000000..8401757
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Book.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1.resource;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Book extends Item {
+
+    public Book() {
+    }
+
+    public Book(final String title, final String author) {
+        super(title, author);
+    }
+
+
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore.java b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore.java
new file mode 100644
index 0000000..f66b7b3
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1.resource;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Path("/")
+@Singleton
+@Template
+@Produces("text/html")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Bookstore {
+
+    private final Map<String, Item> items = new TreeMap<String, Item>();
+    private String name;
+
+    public Bookstore() {
+        setName("Czech Bookstore");
+        getItems().put("1", new Book("Svejk", "Jaroslav Hasek"));
+        getItems().put("2", new Book("Krakatit", "Karel Capek"));
+    }
+
+    @Path("items/{itemid}/")
+    public Item getItem(@PathParam("itemid") String itemid) {
+        Item i = getItems().get(itemid);
+        if (i == null) {
+            throw new NotFoundException(Response
+                    .status(Response.Status.NOT_FOUND)
+                    .entity("Item, " + itemid + ", is not found")
+                    .build());
+        }
+
+        return i;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Bookstore getXml() {
+        return this;
+    }
+
+    public Map<String, Item> getItems() {
+        return items;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Item.java b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Item.java
new file mode 100644
index 0000000..9a01b41
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Item.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Template
+@Produces("text/html")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Item {
+
+    private String title;
+    private String author;
+
+    public Item() {
+    }
+
+    public Item(final String title, final String author) {
+        this.title = title;
+        this.author = author;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Item getXml() {
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "Item{"
+                + "title='" + title + '\''
+                + ", author='" + author + '\''
+                + '}';
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..7692801
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>org.glassfish.jersey.tests.integration.servlet_25_mvc_1.MyApplication</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_mvc_1.MyApplication</param-value>
+        </init-param>
+        <!-- pass to next filter if Jersey/App returns 404 -->
+        <init-param>
+            <param-name>jersey.config.servlet.filter.forwardOn404</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>org.glassfish.jersey.tests.integration.servlet_25_mvc_1.MyApplication</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Book/index.jsp b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Book/index.jsp
new file mode 100644
index 0000000..bff6020
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Book/index.jsp
@@ -0,0 +1,46 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<%--
+The taglib directive below imports the JSTL library. If you uncomment it,
+you must also add the JSTL library to the project. The Add Library... action
+on Libraries node in Projects view can be used to add the JSTL 1.1 library.
+--%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Book</title>
+    </head>
+    <body>
+
+        <h1>${it.title}</h1>
+
+        Book from ${it.author}
+
+        <rbt:include page="footer.jsp"/>
+
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/count.jsp b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/count.jsp
new file mode 100644
index 0000000..2fe7542
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/count.jsp
@@ -0,0 +1,27 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<%--
+  An example of another side JSP.
+--%>
+<html>
+  <body>
+   # of items: ${fn:length(it.items)} 
+  </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/index.jsp b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/index.jsp
new file mode 100644
index 0000000..2ddb202
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/index.jsp
@@ -0,0 +1,56 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <style type="text/css" media="screen">
+          @import url( <c:url value="/css/style.css"/> );
+        </style>
+        <title>REST Bookstore Sample</title>
+    </head>
+    <body>
+
+    <h1>${it.name}</h1>
+    
+    <h2>Item List</h2>
+
+    <ul>
+        <c:forEach var="i" items="${it.items}">
+            <li><a href="items/${i.key}/">${i.value.title}</a>
+        </c:forEach>
+    </ul>
+    
+    <h2>Others</h2>
+    <p>
+      <a href="count">count inventory</a>
+    <p>
+      <a href="time">get the system time</a>
+    <p>
+      <a href="jsp/help.jsp">regular resources</a>
+    </p>    
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/time.jsp b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/time.jsp
new file mode 100644
index 0000000..527e22f
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Bookstore/time.jsp
@@ -0,0 +1,24 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+  <body>
+    Current system time is ${it.systemTime}
+  </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Item/footer.jsp b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Item/footer.jsp
new file mode 100644
index 0000000..fdff710
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/resource/Item/footer.jsp
@@ -0,0 +1,24 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<p>
+<a href="../../">Back</a>
+<hr>
+<div align="right">
+    Common footer (title ${it.title})
+</div>
diff --git a/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/BookstoreITCase.java b/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/BookstoreITCase.java
new file mode 100644
index 0000000..a751b9c
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/BookstoreITCase.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_1.resource.Bookstore;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class BookstoreITCase extends TestSupport {
+
+    @Test
+    public void testResourceAsHtml() throws Exception {
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        assertBookstoreHtmlResponse(target().request(MediaType.TEXT_HTML_TYPE).get(String.class));
+    }
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final Bookstore response = target().request("application/xml").get(Bookstore.class);
+
+        assertNotNull("Should have returned a bookstore!", response);
+        assertEquals("bookstore name", "Czech Bookstore", response.getName());
+    }
+
+    @Test
+    public void testSingleContentTypeAndContentLengthValueInXmlResponse() throws Exception {
+        assertStatusContentTypeAndLength(target().request("application/xml").get());
+        assertStatusContentTypeAndLength(target()
+                .request("text/html", "application/xhtml+xml", "application/xml;q=0.9", "*/*;q=0.8").get());
+    }
+
+    private void assertStatusContentTypeAndLength(Response response) {
+        assertEquals("Should have returned a 200 response!", 200, response.getStatus());
+        assertTrue("Should contain a Content-Type header!", response.getHeaders().containsKey(HttpHeaders.CONTENT_TYPE));
+        assertEquals("Should have a single Content-Type header!", 1, response.getHeaders().get(HttpHeaders.CONTENT_TYPE).size());
+        assertTrue("Should contain a Content-Length header!", response.getHeaders().containsKey(HttpHeaders.CONTENT_LENGTH));
+        assertEquals("Should have a single Content-Length header!",
+                1, response.getHeaders().get(HttpHeaders.CONTENT_LENGTH).size());
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final String response = target().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(String.class);
+
+        assertBookstoreHtmlResponse(response);
+    }
+
+    protected void assertBookstoreHtmlResponse(String response) {
+        assertHtmlResponse(response);
+        assertResponseContains(response, "Bookstore");
+        assertResponseContains(response, "Item List");
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/ItemITCase.java b/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/ItemITCase.java
new file mode 100644
index 0000000..90d9118
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/ItemITCase.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_1.resource.Book;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class ItemITCase extends TestSupport {
+
+    @Test
+    public void testResourceAsHtml() throws Exception {
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        final String response = item1resource().request(MediaType.TEXT_HTML).get(String.class);
+        assertItemHtmlResponse(response);
+    }
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final String text = item1resource().request("application/xml").get(String.class);
+        System.out.println("Item XML is: " + text);
+
+        final Book response = item1resource().request("application/xml").get(Book.class);
+        assertNotNull("Should have returned an item!", response);
+        assertEquals("item title", "Svejk", response.getTitle());
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final String response = item1resource().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(String.class);
+
+        assertItemHtmlResponse(response);
+    }
+
+    protected void assertItemHtmlResponse(String response) {
+        assertHtmlResponse(response);
+        assertResponseContains(response, "<title>Book</title>");
+        assertResponseContains(response, "<h1>Svejk</h1>");
+    }
+
+    protected WebTarget item1resource() {
+        return target().path("/items/1");
+    }
+
+}
diff --git a/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/TestSupport.java b/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/TestSupport.java
new file mode 100644
index 0000000..0ae958f
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_1/TestSupport.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_1;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * A base class for test cases which boots up a GlassFish server for in container testing of RESTful resources.
+ *
+ * @author James Strachan
+ * @author Naresh
+ */
+public abstract class TestSupport extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    protected void assertHtmlResponse(String response) {
+        assertNotNull("No text returned!", response);
+
+        assertResponseContains(response, "<html>");
+        assertResponseContains(response, "</html>");
+    }
+
+    protected void assertResponseContains(String response, String text) {
+        assertTrue("Response should contain " + text + " but was: " + response, response.contains(text));
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/pom.xml b/tests/integration/servlet-2.5-mvc-2/pom.xml
new file mode 100644
index 0000000..a34c50d
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-mvc-2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-mvc-2</name>
+
+    <description>Servlet integration test - servlet-2.5-mvc-2 - filter - MVC - jersey.config.servlet.jsp.disableJspTemplateProcessor</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-jsp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/MyApplication.java b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/MyApplication.java
new file mode 100644
index 0000000..b3f7066
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/MyApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_2.resource.Bookstore;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        // Resources.
+        packages(Bookstore.class.getPackage().getName());
+
+        // MVC.
+        register(JspMvcFeature.class);
+
+        // Logging.
+        register(LoggingFeature.class);
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Book.java b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Book.java
new file mode 100644
index 0000000..5b64e83
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Book.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2.resource;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Book extends Item {
+
+    public Book() {
+    }
+
+    public Book(final String title, final String author) {
+        super(title, author);
+    }
+
+
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore.java b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore.java
new file mode 100644
index 0000000..aeb0812
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2.resource;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Path("/")
+@Singleton
+@Template
+@Produces("text/html")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Bookstore {
+
+    private final Map<String, Item> items = new TreeMap<String, Item>();
+    private String name;
+
+    public Bookstore() {
+        setName("Czech Bookstore");
+        getItems().put("1", new Book("Svejk", "Jaroslav Hasek"));
+        getItems().put("2", new Book("Krakatit", "Karel Capek"));
+    }
+
+    @Path("items/{itemid}/")
+    public Item getItem(@PathParam("itemid") String itemid) {
+        Item i = getItems().get(itemid);
+        if (i == null) {
+            throw new NotFoundException(Response
+                    .status(Response.Status.NOT_FOUND)
+                    .entity("Item, " + itemid + ", is not found")
+                    .build());
+        }
+
+        return i;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Bookstore getXml() {
+        return this;
+    }
+
+    public Map<String, Item> getItems() {
+        return items;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Item.java b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Item.java
new file mode 100644
index 0000000..42ef066
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Item.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Template
+@Produces("text/html")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Item {
+
+    private String title;
+    private String author;
+
+    public Item() {
+    }
+
+    public Item(final String title, final String author) {
+        this.title = title;
+        this.author = author;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Item getXml() {
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "Item{"
+                + "title='" + title + '\''
+                + ", author='" + author + '\''
+                + '}';
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..4fa05ba
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>org.glassfish.jersey.tests.integration.servlet_25_mvc_2.MyApplication</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_mvc_2.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.mvc.templateBasePath.jsp</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <!-- pass to next filter if Jersey/App returns 404 -->
+        <init-param>
+            <param-name>jersey.config.servlet.filter.forwardOn404</param-name>
+            <param-value>false</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>org.glassfish.jersey.tests.integration.servlet_25_mvc_2.MyApplication</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Book/index.jsp b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Book/index.jsp
new file mode 100644
index 0000000..bff6020
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Book/index.jsp
@@ -0,0 +1,46 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<%--
+The taglib directive below imports the JSTL library. If you uncomment it,
+you must also add the JSTL library to the project. The Add Library... action
+on Libraries node in Projects view can be used to add the JSTL 1.1 library.
+--%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Book</title>
+    </head>
+    <body>
+
+        <h1>${it.title}</h1>
+
+        Book from ${it.author}
+
+        <rbt:include page="footer.jsp"/>
+
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/count.jsp b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/count.jsp
new file mode 100644
index 0000000..2fe7542
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/count.jsp
@@ -0,0 +1,27 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<%--
+  An example of another side JSP.
+--%>
+<html>
+  <body>
+   # of items: ${fn:length(it.items)} 
+  </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/index.jsp b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/index.jsp
new file mode 100644
index 0000000..2ddb202
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/index.jsp
@@ -0,0 +1,56 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <style type="text/css" media="screen">
+          @import url( <c:url value="/css/style.css"/> );
+        </style>
+        <title>REST Bookstore Sample</title>
+    </head>
+    <body>
+
+    <h1>${it.name}</h1>
+    
+    <h2>Item List</h2>
+
+    <ul>
+        <c:forEach var="i" items="${it.items}">
+            <li><a href="items/${i.key}/">${i.value.title}</a>
+        </c:forEach>
+    </ul>
+    
+    <h2>Others</h2>
+    <p>
+      <a href="count">count inventory</a>
+    <p>
+      <a href="time">get the system time</a>
+    <p>
+      <a href="jsp/help.jsp">regular resources</a>
+    </p>    
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/time.jsp b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/time.jsp
new file mode 100644
index 0000000..527e22f
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Bookstore/time.jsp
@@ -0,0 +1,24 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+  <body>
+    Current system time is ${it.systemTime}
+  </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Item/footer.jsp b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Item/footer.jsp
new file mode 100644
index 0000000..fdff710
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/main/webapp/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/resource/Item/footer.jsp
@@ -0,0 +1,24 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<p>
+<a href="../../">Back</a>
+<hr>
+<div align="right">
+    Common footer (title ${it.title})
+</div>
diff --git a/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/BookstoreITCase.java b/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/BookstoreITCase.java
new file mode 100644
index 0000000..0d20972
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/BookstoreITCase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_2.resource.Bookstore;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class BookstoreITCase extends TestSupport {
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final Bookstore response = target().request("application/xml").get(Bookstore.class);
+
+        assertBookstoreXmlResponse(response);
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final Response response = target().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(Response.class);
+
+        assertEquals(404, response.getStatus());
+    }
+
+    protected void assertBookstoreXmlResponse(final Bookstore response) {
+        assertNotNull("Should have returned a bookstore!", response);
+        assertEquals("bookstore name", "Czech Bookstore", response.getName());
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/ItemITCase.java b/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/ItemITCase.java
new file mode 100644
index 0000000..e4a793b
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/ItemITCase.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_2.resource.Book;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class ItemITCase extends TestSupport {
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final String text = item1resource().request("application/xml").get(String.class);
+        System.out.println("Item XML is: " + text);
+
+        final Book response = item1resource().request("application/xml").get(Book.class);
+        assertItemXmlResponse(response);
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final Response response = item1resource().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(Response.class);
+
+        assertEquals(404, response.getStatus());
+    }
+
+    private void assertItemXmlResponse(final Book response) {
+        assertNotNull("Should have returned an item!", response);
+        assertEquals("item title", "Svejk", response.getTitle());
+    }
+
+    protected WebTarget item1resource() {
+        return target().path("/items/1");
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/TestSupport.java b/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/TestSupport.java
new file mode 100644
index 0000000..d9d2099
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_2/TestSupport.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_2;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+/**
+ * A base class for test cases which boots up a GlassFish server for in container testing of RESTful resources.
+ *
+ * @author James Strachan
+ * @author Naresh
+ */
+public abstract class TestSupport extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/pom.xml b/tests/integration/servlet-2.5-mvc-3/pom.xml
new file mode 100644
index 0000000..962b2ba
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-mvc-3</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-mvc-3</name>
+
+    <description>Servlet integration test - servlet-2.5-mvc-3 - filter - MVC - jersey.config.servlet.JspTemplatesBasePath</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-mvc-jsp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/MyApplication.java b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/MyApplication.java
new file mode 100644
index 0000000..998b5d4
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/MyApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_3.resource.Bookstore;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        // Resources.
+        packages(Bookstore.class.getPackage().getName());
+
+        // MVC.
+        register(JspMvcFeature.class);
+
+        // Logging.
+        register(LoggingFeature.class);
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book.java b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book.java
new file mode 100644
index 0000000..049a8f2
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3.resource;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Book extends Item {
+
+    public Book() {
+    }
+
+    public Book(final String title, final String author) {
+        super(title, author);
+    }
+
+
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore.java b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore.java
new file mode 100644
index 0000000..d52888b
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3.resource;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Singleton;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+
+@Path("/")
+@Singleton
+@Template
+@Produces("text/html")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Bookstore {
+
+    private final Map<String, Item> items = new TreeMap<String, Item>();
+    private String name;
+
+    public Bookstore() {
+        setName("Czech Bookstore");
+        getItems().put("1", new Book("Svejk", "Jaroslav Ha\u0161ek"));
+        getItems().put("2", new Book("Krakatit", "Karel Capek"));
+    }
+
+    @Path("items/{itemid}/")
+    public Item getItem(@PathParam("itemid") String itemid) {
+        Item i = getItems().get(itemid);
+        if (i == null) {
+            throw new NotFoundException(Response
+                    .status(Response.Status.NOT_FOUND)
+                    .entity("Item, " + itemid + ", is not found")
+                    .build());
+        }
+
+        return i;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Bookstore getXml() {
+        return this;
+    }
+
+    public Map<String, Item> getItems() {
+        return items;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Item.java b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Item.java
new file mode 100644
index 0000000..85eb6cb
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Item.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.server.mvc.Template;
+import org.glassfish.jersey.server.mvc.Viewable;
+
+@Template
+@Produces("text/html")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Item {
+
+    private String title;
+    private String author;
+
+    public Item() {
+    }
+
+    public Item(final String title, final String author) {
+        this.title = title;
+        this.author = author;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    @GET
+    @Produces({MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON})
+    public Item getXml() {
+        return this;
+    }
+
+    @GET
+    @Path("utf")
+    public Viewable getItemUtf8() {
+        return new Viewable("index.utf8.jsp", this);
+    }
+
+    @GET
+    @Path("iso")
+    public Viewable getItemIso88592() {
+        return new Viewable("index.iso88592.jsp", this);
+    }
+
+    @Override
+    public String toString() {
+        return "Item{"
+                + "title='" + title + '\''
+                + ", author='" + author + '\''
+                + '}';
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..bcc550e
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>org.glassfish.jersey.tests.integration.servlet_25_mvc_3.MyApplication</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_mvc_3.MyApplication</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.mvc.templateBasePath.jsp</param-name>
+            <param-value>/jsp/</param-value>
+        </init-param>
+        <!-- pass to next filter if Jersey/App returns 404 -->
+        <init-param>
+            <param-name>jersey.config.servlet.filter.forwardOn404</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>org.glassfish.jersey.tests.integration.servlet_25_mvc_3.MyApplication</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.iso88592.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.iso88592.jsp
new file mode 100644
index 0000000..57643b6
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.iso88592.jsp
@@ -0,0 +1,45 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="ISO-8859-2"%>
+<%--
+The taglib directive below imports the JSTL library. If you uncomment it,
+you must also add the JSTL library to the project. The Add Library... action
+on Libraries node in Projects view can be used to add the JSTL 1.1 library.
+--%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <title>Book</title>
+    </head>
+    <body>
+
+        <h1>${it.title}</h1>
+
+        Book from ${it.author}
+
+        <rbt:include page="footer.jsp"/>
+
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.jsp
new file mode 100644
index 0000000..bff6020
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.jsp
@@ -0,0 +1,46 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<%--
+The taglib directive below imports the JSTL library. If you uncomment it,
+you must also add the JSTL library to the project. The Add Library... action
+on Libraries node in Projects view can be used to add the JSTL 1.1 library.
+--%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <title>Book</title>
+    </head>
+    <body>
+
+        <h1>${it.title}</h1>
+
+        Book from ${it.author}
+
+        <rbt:include page="footer.jsp"/>
+
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.utf8.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.utf8.jsp
new file mode 100644
index 0000000..3410c60
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Book/index.utf8.jsp
@@ -0,0 +1,45 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<%--
+The taglib directive below imports the JSTL library. If you uncomment it,
+you must also add the JSTL library to the project. The Add Library... action
+on Libraries node in Projects view can be used to add the JSTL 1.1 library.
+--%>
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@taglib prefix="rbt" uri="urn:org:glassfish:jersey:servlet:mvc" %>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <title>Book</title>
+    </head>
+    <body>
+
+        <h1>${it.title}</h1>
+
+        Book from ${it.author}
+
+        <rbt:include page="footer.jsp"/>
+
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/count.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/count.jsp
new file mode 100644
index 0000000..2fe7542
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/count.jsp
@@ -0,0 +1,27 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<%--
+  An example of another side JSP.
+--%>
+<html>
+  <body>
+   # of items: ${fn:length(it.items)} 
+  </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/index.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/index.jsp
new file mode 100644
index 0000000..91796e2
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/index.jsp
@@ -0,0 +1,56 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+   "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <style type="text/css" media="screen">
+          @import url( <c:url value="/css/style.css"/> );
+        </style>
+        <title>REST Bookstore Sample</title>
+    </head>
+    <body>
+
+    <h1>${it.name}</h1>
+
+    <h2>Item List</h2>
+
+    <ul>
+        <c:forEach var="i" items="${it.items}">
+            <li><a href="items/${i.key}/">${i.value.title}</a>
+        </c:forEach>
+    </ul>
+
+    <h2>Others</h2>
+    <p>
+      <a href="count">count inventory</a>
+    <p>
+      <a href="time">get the system time</a>
+    <p>
+      <a href="jsp/help.jsp">regular resources</a>
+    </p>
+    </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/time.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/time.jsp
new file mode 100644
index 0000000..527e22f
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Bookstore/time.jsp
@@ -0,0 +1,24 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+  <body>
+    Current system time is ${it.systemTime}
+  </body>
+</html>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Item/footer.jsp b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Item/footer.jsp
new file mode 100644
index 0000000..fdff710
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/main/webapp/jsp/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/resource/Item/footer.jsp
@@ -0,0 +1,24 @@
+<%--
+
+    Copyright (c) 2010, 2018 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
+
+--%>
+
+<p>
+<a href="../../">Back</a>
+<hr>
+<div align="right">
+    Common footer (title ${it.title})
+</div>
diff --git a/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/BookstoreITCase.java b/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/BookstoreITCase.java
new file mode 100644
index 0000000..11f57e4
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/BookstoreITCase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3;
+
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_3.resource.Bookstore;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class BookstoreITCase extends TestSupport {
+
+    @Test
+    public void testResourceAsHtml() throws Exception {
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        assertBookstoreHtmlResponse(target().request(MediaType.TEXT_HTML).get(String.class));
+    }
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final Bookstore response = target().request("application/xml").get(Bookstore.class);
+
+        assertNotNull("Should have returned a bookstore!", response);
+        assertEquals("bookstore name", "Czech Bookstore", response.getName());
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final String response = target().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(String.class);
+
+        assertBookstoreHtmlResponse(response);
+    }
+
+    protected void assertBookstoreHtmlResponse(String response) {
+        assertHtmlResponse(response);
+        assertResponseContains(response, "Bookstore");
+        assertResponseContains(response, "Item List");
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/ItemITCase.java b/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/ItemITCase.java
new file mode 100644
index 0000000..b3c3e39
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/ItemITCase.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.tests.integration.servlet_25_mvc_3.resource.Book;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+public class ItemITCase extends TestSupport {
+
+    @Test
+    public void testResourceAsHtml() throws Exception {
+        // NOTE: HttpUrlConnector sends several accepted types by default when not explicitly set by the caller.
+        // In such case, the .accept("text/html") call is not necessary. However, other connectors act in a different way and
+        // this leads in different behaviour when selecting the MessageBodyWriter. Leaving the definition explicit for broader
+        // compatibility.
+        final String response = item1resource().request(MediaType.TEXT_HTML).get(String.class);
+        assertItemHtmlResponse(response);
+    }
+
+    @Test
+    public void testResourceAsHtmlUtf8() throws Exception {
+        final Response response = item1resource().path("utf").request().get();
+        final String html = response.readEntity(String.class);
+
+        assertItemHtmlResponse(html);
+        assertResponseContains(html, "Ha\u0161ek");
+    }
+
+    @Test
+    public void testResourceAsHtmlIso88592() throws Exception {
+        final Response response = item1resource().path("iso").request().get();
+        response.bufferEntity();
+
+        final String htmlUtf8 = response.readEntity(String.class);
+
+        assertItemHtmlResponse(htmlUtf8);
+        assertFalse("Response shouldn't contain Ha\u0161ek but was: " + htmlUtf8, htmlUtf8.contains("Ha\u0161ek"));
+
+        final byte[] bytes = response.readEntity(byte[].class);
+        final String htmlIso = new String(bytes, "ISO-8859-2");
+
+        assertItemHtmlResponse(htmlIso);
+        assertFalse("Response shouldn't contain Ha\u0161ek but was: " + htmlIso, htmlIso.contains("Ha\u0161ek"));
+        assertResponseContains(htmlIso, new String("Ha\u0161ek".getBytes(), "ISO-8859-2"));
+    }
+
+    @Test
+    public void testResourceAsXml() throws Exception {
+        final String text = item1resource().request("application/xml").get(String.class);
+        System.out.println("Item XML is: " + text);
+
+        final Book response = item1resource().request("application/xml").get(Book.class);
+        assertNotNull("Should have returned an item!", response);
+        assertEquals("item title", "Svejk", response.getTitle());
+    }
+
+    @Test
+    public void testResourceAsHtmlUsingWebKitAcceptHeaders() throws Exception {
+        final String response = item1resource().request(
+                "text/html",
+                "application/xhtml+xml",
+                "application/xml;q=0.9",
+                "*/*;q=0.8").get(String.class);
+
+        assertItemHtmlResponse(response);
+    }
+
+    protected void assertItemHtmlResponse(final String response) {
+        assertHtmlResponse(response);
+        assertResponseContains(response, "<title>Book</title>");
+        assertResponseContains(response, "<h1>Svejk</h1>");
+    }
+
+    protected WebTarget item1resource() {
+        return target().path("/items/1");
+    }
+}
diff --git a/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/TestSupport.java b/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/TestSupport.java
new file mode 100644
index 0000000..69121ee
--- /dev/null
+++ b/tests/integration/servlet-2.5-mvc-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_mvc_3/TestSupport.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_25_mvc_3;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * A base class for test cases which boots up a GlassFish server for in container testing of RESTful resources.
+ *
+ * @author James Strachan
+ * @author Naresh
+ */
+public abstract class TestSupport extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+        return new MyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    protected void assertHtmlResponse(final String response) {
+        assertNotNull("No text returned!", response);
+
+        assertResponseContains(response, "<html>");
+        assertResponseContains(response, "</html>");
+    }
+
+    protected void assertResponseContains(final String response, final String text) {
+        assertTrue("Response should contain " + text + " but was: " + response, response.contains(text));
+    }
+}
diff --git a/tests/integration/servlet-2.5-reload/pom.xml b/tests/integration/servlet-2.5-reload/pom.xml
new file mode 100644
index 0000000..29de897
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-2.5-reload</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-2.5-reload</name>
+
+    <description>Servlet integration test - servlet-2.5-reload - reload resource triggers application reload</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/AnotherResource.java b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/AnotherResource.java
new file mode 100644
index 0000000..49e28ec
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/AnotherResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_config_reload;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("another")
+public class AnotherResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Another resource";
+    }
+}
diff --git a/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/HelloWorldResource.java b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/HelloWorldResource.java
new file mode 100644
index 0000000..2fdcb63
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_config_reload;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World! " + this.getClass().getPackage().getName();
+    }
+}
diff --git a/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadContainerLifecycleListener.java b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadContainerLifecycleListener.java
new file mode 100644
index 0000000..ceee204
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadContainerLifecycleListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_config_reload;
+
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener;
+import org.glassfish.jersey.server.spi.Container;
+
+/**
+ * @author Miroslav Fuksa
+ */
+@Provider
+public class ReloadContainerLifecycleListener extends AbstractContainerLifecycleListener {
+
+    private static Container container;
+
+    @Override
+    public void onStartup(final Container container) {
+        ReloadContainerLifecycleListener.setContainer(container);
+    }
+
+    public static Container getContainer() {
+        return container;
+    }
+
+    public static void setContainer(final Container container) {
+        ReloadContainerLifecycleListener.container = container;
+    }
+
+}
diff --git a/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadResource.java b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadResource.java
new file mode 100644
index 0000000..e2c1c46
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadResource.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_config_reload;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("reload")
+public class ReloadResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        try {
+            ReloadContainerLifecycleListener.getContainer().reload(
+                    new ResourceConfig(HelloWorldResource.class,
+                            ReloadResource.class,
+                            AnotherResource.class,
+                            ReloadContainerLifecycleListener.class)
+            );
+        } catch (final Exception e) {
+            e.printStackTrace();
+        }
+        return "Reload resource";
+    }
+}
diff --git a/tests/integration/servlet-2.5-reload/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-2.5-reload/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..14a0f9c
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>JerseyReloadServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_25_config_reload.ReloadResource org.glassfish.jersey.tests.integration.servlet_25_config_reload.HelloWorldResource
+                org.glassfish.jersey.tests.integration.servlet_25_config_reload.ReloadResource org.glassfish.jersey.tests.integration.servlet_25_config_reload.ReloadContainerLifecycleListener</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JerseyReloadServlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java b/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java
new file mode 100644
index 0000000..af22617
--- /dev/null
+++ b/tests/integration/servlet-2.5-reload/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_config_reload/ReloadTestIT.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_25_config_reload;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class ReloadTestIT extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testReload() throws Exception {
+        Response response = target().path("helloworld").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("Hello World! " + this.getClass().getPackage().getName(), response.readEntity(String.class));
+
+        response = target().path("another").request().get();
+        assertEquals(404, response.getStatus());
+
+        response = target().path("reload").request().get();
+        assertEquals(200, response.getStatus());
+
+        response = target().path("another").request().get();
+        assertEquals(200, response.getStatus());
+    }
+}
diff --git a/tests/integration/servlet-3-async/pom.xml b/tests/integration/servlet-3-async/pom.xml
new file mode 100644
index 0000000..a959490
--- /dev/null
+++ b/tests/integration/servlet-3-async/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-async</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-async</name>
+
+    <description>Servlet integration test - servlet-3-async - configured via jax-rs application annotated with @javax.ws.rs.ApplicationPath</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResource.java b/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResource.java
new file mode 100644
index 0000000..2f96055
--- /dev/null
+++ b/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+/**
+ * Asynchronous servlet-deployed resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("async")
+public class AsyncServletResource {
+    /**
+     * Hello world message.
+     */
+    public static final String HELLO_ASYNC_WORLD = "Hello Async World!";
+    public static final String CANCELED = "Canceled";
+
+    private static BlockingQueue<CanceledRequest> cancelingQueue = new ArrayBlockingQueue<CanceledRequest>(5);
+
+    private static class CanceledRequest {
+        private final String id;
+        private final AsyncResponse asyncResponse;
+
+        private CanceledRequest(String id, AsyncResponse asyncResponse) {
+            this.id = id;
+            this.asyncResponse = asyncResponse;
+        }
+    }
+
+    /**
+     * Get the async "Hello World" message.
+     */
+    @GET
+    @Produces("text/plain")
+    public void get(@Suspended final AsyncResponse ar) {
+        Executors.newSingleThreadExecutor().execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(100);
+                    ar.resume(HELLO_ASYNC_WORLD);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+    }
+
+    /**
+     * Get a canceled request.
+     *
+     * @param id request id.
+     * @throws InterruptedException in case of not being able to put the request
+     *                              to an internal queue for canceling.
+     */
+    @GET
+    @Path("canceled")
+    public void getCanceled(@Suspended final AsyncResponse ar, @QueryParam("id") final String id) throws InterruptedException {
+        cancelingQueue.put(new CanceledRequest(id, ar));
+    }
+
+    /**
+     * Cancel a request that is on top of the canceling queue.
+     *
+     * @return notification message about successful request canceling.
+     * @throws InterruptedException in case of not being able to take a cancelled request
+     *                              from an internal canceling queue.
+     */
+    @POST
+    @Produces("text/plain")
+    @Path("canceled")
+    public String cancel(String requestId) throws InterruptedException {
+        final CanceledRequest canceledRequest = cancelingQueue.take();
+        canceledRequest.asyncResponse.cancel();
+
+        return CANCELED + " " + canceledRequest.id + " by POST " + requestId;
+    }
+
+}
diff --git a/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncTimeoutResource.java b/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncTimeoutResource.java
new file mode 100644
index 0000000..23c000b
--- /dev/null
+++ b/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncTimeoutResource.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.container.TimeoutHandler;
+
+/**
+ * Asynchronous servlet-deployed resource for testing {@link javax.ws.rs.container.AsyncResponse async response} timeouts.
+ *
+ * @author Michal Gajdos
+ */
+@Path("timeout")
+public class AsyncTimeoutResource {
+
+    private static final BlockingQueue<AsyncResponse> queue = new ArrayBlockingQueue<AsyncResponse>(1);
+
+    @GET
+    @Path("suspend")
+    public void suspend(@Suspended final AsyncResponse asyncResponse) {
+        queue.add(asyncResponse);
+    }
+
+    @POST
+    @Path("timeout")
+    public void setTimeOut(final Integer millis) throws InterruptedException {
+        final AsyncResponse asyncResponse = queue.take();
+
+        final boolean timeout1 = asyncResponse.setTimeout(millis, TimeUnit.MILLISECONDS);
+        asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+            @Override
+            public void handleTimeout(final AsyncResponse asyncResponse) {
+                final boolean timeout2 = asyncResponse.setTimeout(millis, TimeUnit.MILLISECONDS);
+                asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+                    @Override
+                    public void handleTimeout(final AsyncResponse asyncResponse) {
+                        asyncResponse.resume("timeout1=" + timeout1 + "_timeout2=" + timeout2 + "_handled");
+                        asyncResponse.cancel();
+                    }
+                });
+            }
+        });
+    }
+}
diff --git a/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/Servlet3Async.java b/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/Servlet3Async.java
new file mode 100644
index 0000000..5787a1d
--- /dev/null
+++ b/tests/integration/servlet-3-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/Servlet3Async.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_async;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Asynchronous servlet-deployed resource application.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("/")
+public class Servlet3Async extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<Class<?>>();
+        hashSet.add(AsyncServletResource.class);
+        hashSet.add(AsyncTimeoutResource.class);
+        return hashSet;
+    }
+}
diff --git a/tests/integration/servlet-3-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResourceITCase.java b/tests/integration/servlet-3-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResourceITCase.java
new file mode 100644
index 0000000..6cdfb9a
--- /dev/null
+++ b/tests/integration/servlet-3-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResourceITCase.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_async;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Asynchronous servlet-deployed resource test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class AsyncServletResourceITCase extends JerseyTest {
+    private static final Logger LOGGER = Logger.getLogger(AsyncServletResourceITCase.class.getName());
+
+    private static class ResponseRecord {
+        final int status;
+        final String message;
+
+        private ResponseRecord(int status, String message) {
+            this.status = status;
+            this.message = message;
+        }
+
+        @Override
+        public String toString() {
+            return status + " : \"" + message + '\"';
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Test asynchronous servlet-deployed resource.
+     *
+     * @throws InterruptedException in case the waiting for all requests to complete was interrupted.
+     */
+    @Test
+    public void testAsyncServlet() throws InterruptedException {
+        final WebTarget resourceTarget = target("async");
+        resourceTarget.register(LoggingFeature.class);
+        final String expectedResponse = AsyncServletResource.HELLO_ASYNC_WORLD;
+
+        final int MAX_MESSAGES = 50;
+        final int LATCH_WAIT_TIMEOUT = 10 * getAsyncTimeoutMultiplier();
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final Object sequentialGetLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-resource-test-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, ResponseRecord> getResponses = new ConcurrentHashMap<Integer, ResponseRecord>();
+
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        //noinspection PointlessBooleanExpression,ConstantConditions
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            final Response response = resourceTarget.request().get();
+                            getResponses.put(
+                                    requestId,
+                                    new ResponseRecord(response.getStatus(), response.readEntity(String.class)));
+                        } catch (Throwable t) {
+                            t.printStackTrace();
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            //noinspection ConstantConditions
+            if (debugMode) {
+                getRequestLatch.await();
+            } else {
+                assertTrue("Waiting for all GET requests to complete has timed out.", getRequestLatch.await(LATCH_WAIT_TIMEOUT,
+                        TimeUnit.SECONDS));
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        StringBuilder messageBuilder = new StringBuilder();
+        for (Map.Entry<Integer, ResponseRecord> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append("GET response for message ")
+                    .append(getResponseEntry.getKey()).append(": ")
+                    .append(getResponseEntry.getValue().toString()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        assertEquals(MAX_MESSAGES, getResponses.size());
+        for (Map.Entry<Integer, ResponseRecord> entry : getResponses.entrySet()) {
+            assertEquals(
+                    "Unexpected GET response status for request " + entry.getKey(),
+                    200, entry.getValue().status);
+            assertEquals(
+                    "Unexpected GET response message for request " + entry.getKey(),
+                    expectedResponse, entry.getValue().message);
+        }
+    }
+
+    /**
+     * Test canceling of an async request to a servlet-deployed resource.
+     *
+     * @throws InterruptedException in case the waiting for all requests to complete was interrupted.
+     */
+    @Test
+    public void testAsyncRequestCanceling() throws InterruptedException {
+        final WebTarget resourceTarget = target("async/canceled");
+        resourceTarget.register(LoggingFeature.class);
+
+        final int MAX_MESSAGES = 10;
+        final int LATCH_WAIT_TIMEOUT = 10 * getAsyncTimeoutMultiplier();
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final boolean sequentialPost = false;
+        final Object sequentialGetLock = new Object();
+        final Object sequentialPostLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-canceled-resource-test-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, String> postResponses = new ConcurrentHashMap<Integer, String>();
+        final Map<Integer, String> getResponses = new ConcurrentHashMap<Integer, String>();
+
+        final CountDownLatch postRequestLatch = new CountDownLatch(MAX_MESSAGES);
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        //noinspection PointlessBooleanExpression,ConstantConditions
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            final String response = resourceTarget.queryParam("id", requestId).request().get(String.class);
+                            getResponses.put(requestId, response);
+                        } catch (WebApplicationException ex) {
+                            final Response response = ex.getResponse();
+                            getResponses.put(requestId, response.getStatus() + ": " + response.readEntity(String.class));
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        //noinspection PointlessBooleanExpression,ConstantConditions
+                        if (debugMode || sequentialPost) {
+                            synchronized (sequentialPostLock) {
+                                post();
+                            }
+                        } else {
+                            post();
+                        }
+                    }
+
+                    private void post() throws ProcessingException {
+                        try {
+                            final String response = resourceTarget.request().post(Entity.text("" + requestId), String.class);
+                            postResponses.put(requestId, response);
+                        } finally {
+                            postRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            //noinspection ConstantConditions
+            if (debugMode) {
+                postRequestLatch.await();
+                getRequestLatch.await();
+            } else {
+                assertTrue("Waiting for all POST requests to complete has timed out.",
+                        postRequestLatch.await(LATCH_WAIT_TIMEOUT, TimeUnit.SECONDS));
+                assertTrue("Waiting for all GET requests to complete has timed out.", getRequestLatch.await(LATCH_WAIT_TIMEOUT,
+                        TimeUnit.SECONDS));
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        StringBuilder messageBuilder = new StringBuilder();
+        for (Map.Entry<Integer, String> postResponseEntry : postResponses.entrySet()) {
+            messageBuilder.append("POST response for message ")
+                    .append(postResponseEntry.getKey()).append(": ")
+                    .append(postResponseEntry.getValue()).append('\n');
+        }
+        messageBuilder.append('\n');
+        for (Map.Entry<Integer, String> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append("GET response for message ")
+                    .append(getResponseEntry.getKey()).append(": ")
+                    .append(getResponseEntry.getValue()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        assertEquals(MAX_MESSAGES, postResponses.size());
+        for (Map.Entry<Integer, String> postResponseEntry : postResponses.entrySet()) {
+            assertTrue("Unexpected POST notification response for message " + postResponseEntry.getKey(),
+                    postResponseEntry.getValue().startsWith(AsyncServletResource.CANCELED));
+        }
+
+        assertEquals(MAX_MESSAGES, getResponses.size());
+        final Collection<Integer> getResponseKeys = getResponses.keySet();
+        for (int i = 0; i < MAX_MESSAGES; i++) {
+            assertTrue("Detected a GET message response loss: " + i, getResponseKeys.contains(i));
+            final String getResponseEntry = getResponses.get(i);
+            assertTrue("Unexpected canceled GET response status for request " + i,
+                    getResponseEntry.startsWith("503: "));
+        }
+    }
+}
diff --git a/tests/integration/servlet-3-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncTimeoutResourceITCase.java b/tests/integration/servlet-3-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncTimeoutResourceITCase.java
new file mode 100644
index 0000000..8bfb646
--- /dev/null
+++ b/tests/integration/servlet-3-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncTimeoutResourceITCase.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.Future;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Asynchronous servlet-deployed resource for testing {@link javax.ws.rs.container.AsyncResponse async response} timeouts.
+ *
+ * @author Michal Gajdos
+ */
+public class AsyncTimeoutResourceITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testTimeout() throws Exception {
+        final WebTarget resourceTarget = target("timeout");
+        resourceTarget.register(LoggingFeature.class);
+        final Future<Response> responseFuture = resourceTarget.path("suspend").request().async().get();
+
+        // Set timeout.
+        assertThat(resourceTarget.path("timeout").request().post(Entity.text("500")).getStatus(), equalTo(204));
+
+        // Wait for it.
+        assertThat(responseFuture.get().readEntity(String.class), equalTo("timeout1=true_timeout2=true_handled"));
+    }
+}
diff --git a/tests/integration/servlet-3-chunked-io/pom.xml b/tests/integration/servlet-3-chunked-io/pom.xml
new file mode 100644
index 0000000..4caa4b5
--- /dev/null
+++ b/tests/integration/servlet-3-chunked-io/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-chunked-io</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-chunked-io</name>
+
+    <description>Servlet integration test - servlet-3-chunked-io</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/App.java b/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/App.java
new file mode 100644
index 0000000..2495c51
--- /dev/null
+++ b/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/App.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_chunked_io;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.ext.ContextResolver;
+
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Chunked I/O test JAX-RS application class.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("resources")
+public class App extends ResourceConfig {
+
+    /**
+     * Chunked I/O test JAX-RS application.
+     */
+    public App() {
+        super(TestResource.class);
+
+        register(createMoxyJsonResolver());
+    }
+
+    /**
+     * Create MOXy JSON config context resolver.
+     *
+     * @return new MOXy JSON config context resolver.
+     */
+    public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
+        final Map<String, String> namespacePrefixMapper = new HashMap<>(1);
+        namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+
+        return new MoxyJsonConfig()
+                .setNamespacePrefixMapper(namespacePrefixMapper)
+                .setNamespaceSeparator(':')
+                .resolver();
+    }
+
+}
diff --git a/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/Message.java b/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/Message.java
new file mode 100644
index 0000000..e8b3304
--- /dev/null
+++ b/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/Message.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_chunked_io;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Message POJO.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Message {
+
+    /**
+     * Message id.
+     */
+    public int id;
+    /**
+     * Message content.
+     */
+    public String data;
+
+    /**
+     * Create new message.
+     */
+    public Message() {
+        this.id = -1;
+        this.data = "";
+    }
+
+    /**
+     * Create new message.
+     * @param id message id.
+     * @param data message content.
+     */
+    public Message(int id, String data) {
+        if (data == null) {
+            throw new IllegalArgumentException("Message data must not be null.");
+        }
+
+        this.id = id;
+        this.data = data;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Message message = (Message) o;
+
+        if (id != message.id) {
+            return false;
+        }
+        if (!data.equals(message.data)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id;
+        result = 31 * result + data.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "{\"id\":" + id + ",\"data\":\"" + data + "\"}";
+    }
+}
diff --git a/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/TestResource.java b/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/TestResource.java
new file mode 100644
index 0000000..6069a99
--- /dev/null
+++ b/tests/integration/servlet-3-chunked-io/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/TestResource.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_chunked_io;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.server.ChunkedOutput;
+
+/**
+ * Test resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("/test")
+public class TestResource {
+
+    private static final Logger LOGGER = Logger.getLogger(TestResource.class.getName());
+
+    /**
+     * Get chunk stream of JSON data - from JSON POJOs.
+     *
+     * @return chunk stream.
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("from-pojo")
+    public ChunkedOutput<Message> getFromPojo() {
+        final ChunkedOutput<Message> output = new ChunkedOutput<>(Message.class, "\r\n");
+
+        new Thread() {
+            @Override
+            public void run() {
+                try {
+                    for (int i = 0; i < 3; i++) {
+                        output.write(new Message(i, "test"));
+                        Thread.sleep(200);
+                    }
+                } catch (final IOException e) {
+                    LOGGER.log(Level.SEVERE, "Error writing chunk.", e);
+                } catch (final InterruptedException e) {
+                    LOGGER.log(Level.SEVERE, "Sleep interrupted.", e);
+                    Thread.currentThread().interrupt();
+                } finally {
+                    try {
+                        output.close();
+                    } catch (final IOException e) {
+                        LOGGER.log(Level.INFO, "Error closing chunked output.", e);
+                    }
+                }
+            }
+        }.start();
+
+        return output;
+    }
+
+    /**
+     * Get chunk stream of JSON data - from string.
+     *
+     * @return chunk stream.
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("from-string")
+    public ChunkedOutput<String> getFromText() {
+        final ChunkedOutput<String> output = new ChunkedOutput<>(String.class);
+
+        new Thread() {
+            @Override
+            public void run() {
+                try {
+                    for (int i = 0; i < 3; i++) {
+                        output.write(new Message(i, "test").toString() + "\r\n");
+                        Thread.sleep(200);
+                    }
+                } catch (final IOException e) {
+                    LOGGER.log(Level.SEVERE, "Error writing chunk.", e);
+                } catch (final InterruptedException e) {
+                    LOGGER.log(Level.SEVERE, "Sleep interrupted.", e);
+                    Thread.currentThread().interrupt();
+                } finally {
+                    try {
+                        output.close();
+                    } catch (final IOException e) {
+                        LOGGER.log(Level.INFO, "Error closing chunked output.", e);
+                    }
+                }
+            }
+        }.start();
+
+        return output;
+    }
+
+    /**
+     * {@link org.glassfish.jersey.server.ChunkedOutput#close()} is called before method returns it's entity. Resource reproduces
+     * JERSEY-2558 issue.
+     *
+     * @return (closed) chunk stream.
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("close-before-return")
+    public ChunkedOutput<Message> closeBeforeReturn() {
+        final ChunkedOutput<Message> output = new ChunkedOutput<>(Message.class, "\r\n");
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        new Thread() {
+            @Override
+            public void run() {
+                try {
+                    for (int i = 0; i < 3; i++) {
+                        output.write(new Message(i, "test"));
+                        Thread.sleep(200);
+                    }
+                } catch (final IOException e) {
+                    LOGGER.log(Level.SEVERE, "Error writing chunk.", e);
+                } catch (final InterruptedException e) {
+                    LOGGER.log(Level.SEVERE, "Sleep interrupted.", e);
+                    Thread.currentThread().interrupt();
+                } finally {
+                    try {
+                        output.close();
+                        // Worker thread can continue.
+                        latch.countDown();
+                    } catch (final IOException e) {
+                        LOGGER.log(Level.INFO, "Error closing chunked output.", e);
+                    }
+                }
+            }
+        }.start();
+
+        try {
+            // Wait till new thread closes the chunked output.
+            latch.await();
+            return output;
+        } catch (final InterruptedException e) {
+            throw new InternalServerErrorException(e);
+        }
+    }
+
+    /**
+     * Test combination of AsyncResponse and ChunkedOutput.
+     *
+     * @param response async response.
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("chunked-async")
+    public void closeBeforeReturnAsync(@Suspended final AsyncResponse response) {
+        // Set timeout to able to resume but not send the chunks (setting the ChunkedOutput should remove the timeout).
+        response.setTimeout(1500, TimeUnit.SECONDS);
+
+        new Thread() {
+
+            @Override
+            public void run() {
+                final ChunkedOutput<Message> output = new ChunkedOutput<>(Message.class, "\r\n");
+
+                try {
+                    // Let the method return.
+                    Thread.sleep(1000);
+                    // Resume.
+                    response.resume(output);
+                    // Wait for resume to complete.
+                    Thread.sleep(1000);
+
+                    new Thread() {
+
+                        @Override
+                        public void run() {
+                            try {
+                                for (int i = 0; i < 3; i++) {
+                                    output.write(new Message(i, "test"));
+                                    Thread.sleep(200);
+                                }
+                            } catch (final IOException e) {
+                                LOGGER.log(Level.SEVERE, "Error writing chunk.", e);
+                            } catch (final InterruptedException e) {
+                                LOGGER.log(Level.SEVERE, "Sleep interrupted.", e);
+                                Thread.currentThread().interrupt();
+                            } finally {
+                                try {
+                                    output.close();
+                                } catch (final IOException e) {
+                                    LOGGER.log(Level.INFO, "Error closing chunked output.", e);
+                                }
+                            }
+                        }
+                    }.start();
+                } catch (final InterruptedException e) {
+                    LOGGER.log(Level.SEVERE, "Sleep interrupted.", e);
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }.start();
+    }
+}
diff --git a/tests/integration/servlet-3-chunked-io/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/ChunkedInputOutputITCase.java b/tests/integration/servlet-3-chunked-io/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/ChunkedInputOutputITCase.java
new file mode 100644
index 0000000..40d57af
--- /dev/null
+++ b/tests/integration/servlet-3-chunked-io/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_chunked_io/ChunkedInputOutputITCase.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_chunked_io;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URLConnection;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ChunkedInput;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Chunked I/O integration tests.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class ChunkedInputOutputITCase extends JerseyTest {
+
+    private static final int MAX_LISTENERS = 5;
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+        config.register(App.createMoxyJsonResolver());
+
+        config.property(ClientProperties.CONNECT_TIMEOUT, 15000)
+                .property(ClientProperties.READ_TIMEOUT, 5000)
+                .property(ClientProperties.ASYNC_THREADPOOL_SIZE, MAX_LISTENERS + 1)
+                .connectorProvider(new GrizzlyConnectorProvider());
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        final UriBuilder baseUriBuilder = UriBuilder.fromUri(super.getBaseUri());
+        final boolean externalFactoryInUse = getTestContainerFactory() instanceof ExternalTestContainerFactory;
+        return externalFactoryInUse ? baseUriBuilder.path("resources").build() : baseUriBuilder.build();
+    }
+
+    /**
+     * Test retrieving string-based chunked stream as a single response string.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testChunkedOutputToSingleString() throws Exception {
+        final String response = target().path("test/from-string").request(MediaType.APPLICATION_JSON_TYPE).get(String.class);
+
+        assertEquals("Unexpected value of chunked response unmarshalled as a single string.",
+                "{\"id\":0,\"data\":\"test\"}\r\n"
+                        + "{\"id\":1,\"data\":\"test\"}\r\n"
+                        + "{\"id\":2,\"data\":\"test\"}\r\n",
+                response);
+    }
+
+    /**
+     * Test retrieving string-based chunked stream sequentially as individual chunks using chunked input.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testChunkedOutputToChunkInputFromString() throws Exception {
+        final ChunkedInput<Message> input = target().path("test/from-string").request(MediaType.APPLICATION_JSON_TYPE)
+                .get(new GenericType<ChunkedInput<Message>>() {
+                });
+
+        int counter = 0;
+        Message chunk;
+        while ((chunk = input.read()) != null) {
+            assertEquals("Unexpected value of chunk " + counter, new Message(counter, "test"), chunk);
+            counter++;
+        }
+
+        assertEquals("Unexpected numbed of received chunks.", 3, counter);
+    }
+
+    /**
+     * Test retrieving POJO-based chunked stream sequentially as individual chunks using chunked input.
+     *
+     * @throws Exception in case of a failure during the test execution.
+     */
+    @Test
+    public void testChunkedOutputToChunkInputFromPojo() throws Exception {
+        final ChunkedInput<Message> input = target().path("test/from-pojo").request(MediaType.APPLICATION_JSON_TYPE)
+                .get(new GenericType<ChunkedInput<Message>>() {
+                });
+
+        int counter = 0;
+        Message chunk;
+        while ((chunk = input.read()) != null) {
+            assertEquals("Unexpected value of chunk " + counter, new Message(counter, "test"), chunk);
+            counter++;
+        }
+
+        assertEquals("Unexpected numbed of received chunks.", 3, counter);
+    }
+
+    /**
+     * Test combination of AsyncResponse and ChunkedOutput.
+     */
+    @Test
+    public void chunkedOutputWithAsyncResponse() throws Exception {
+        final ChunkedInput<Message> input = target().path("test/chunked-async").request(MediaType.APPLICATION_JSON_TYPE)
+                .get(new GenericType<ChunkedInput<Message>>() {
+                });
+
+        int counter = 0;
+        Message chunk;
+        while ((chunk = input.read()) != null) {
+            assertEquals("Unexpected value of chunk " + counter, new Message(counter, "test"), chunk);
+            counter++;
+        }
+
+        assertEquals("Unexpected numbed of received chunks.", 3, counter);
+    }
+
+    /**
+     * Reproducer for JERSEY-2558. Checking that the connection is properly closed even when the
+     * {@link org.glassfish.jersey.server.ChunkedOutput#close()} is called before the response is processed by the runtime.
+     */
+    @Test
+    public void checkConnectionIsClosedUrlConnection() throws Exception {
+        final URI uri = UriBuilder.fromUri(super.getBaseUri()).path("test/close-before-return").build();
+        final URLConnection connection = uri.toURL().openConnection();
+
+        connection.setConnectTimeout(15000);
+        connection.setReadTimeout(15000);
+        connection.connect();
+
+        final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+
+        String line;
+        int counter = 0;
+        while ((line = reader.readLine()) != null) {
+            assertEquals("Unexpected value of chunk " + counter, new Message(counter, "test").toString(), line);
+            counter++;
+        }
+
+        assertEquals("Unexpected numbed of received chunks.", 3, counter);
+    }
+}
diff --git a/tests/integration/servlet-3-filter/pom.xml b/tests/integration/servlet-3-filter/pom.xml
new file mode 100644
index 0000000..1bbe427
--- /dev/null
+++ b/tests/integration/servlet-3-filter/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-filter</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-filter</name>
+
+    <description>Servlet integration test - servlet-3-filter</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-filter/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_filter/MyResource.java b/tests/integration/servlet-3-filter/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_filter/MyResource.java
new file mode 100644
index 0000000..ca3c3a7
--- /dev/null
+++ b/tests/integration/servlet-3-filter/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_filter/MyResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_filter;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Path("myresource")
+public class MyResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "OK";
+    }
+}
diff --git a/tests/integration/servlet-3-filter/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-filter/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3dcf4e4
--- /dev/null
+++ b/tests/integration/servlet-3-filter/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <filter>
+        <filter-name>Jersey Web Application</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_3_filter</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>Jersey Web Application</filter-name>
+        <url-pattern>/myapp/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-filter/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_filter/MyResourceITCase.java b/tests/integration/servlet-3-filter/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_filter/MyResourceITCase.java
new file mode 100644
index 0000000..226ac74
--- /dev/null
+++ b/tests/integration/servlet-3-filter/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_filter/MyResourceITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_filter;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class MyResourceITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig rc = new ResourceConfig();
+        return rc.packages(MyResourceITCase.class.getPackage().getName());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testMyResource() throws Exception {
+        final Response response = target().path("myapp").path("myresource").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("OK", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testWadl() {
+        final Response response = target().path("myapp/application.wadl").request().get();
+        assertEquals(200, response.getStatus());
+        assertTrue(response.readEntity(String.class).startsWith("<?xml version=\"1.0\""));
+    }
+}
diff --git a/tests/integration/servlet-3-gf-async/pom.xml b/tests/integration/servlet-3-gf-async/pom.xml
new file mode 100644
index 0000000..9d53935
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-gf-async</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-gf-async</name>
+
+    <description>Servlet (GF) integration test - servlet-3-gf-async - configured via jax-rs application annotated with @javax.ws.rs.ApplicationPath</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>${skipTests}</skipTests>
+                    <systemPropertyVariables>
+                        <jersey.config.test.container.factory>${testContainerFactory}</jersey.config.test.container.factory>
+                        <jersey.config.test.container.port>${testContainerPort}</jersey.config.test.container.port>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+        <skipTests>true</skipTests>
+        <testContainerFactory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</testContainerFactory>
+        <testContainerPort>8080</testContainerPort>
+    </properties>
+</project>
diff --git a/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncCancelTimeoutResource.java b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncCancelTimeoutResource.java
new file mode 100644
index 0000000..f67f854
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncCancelTimeoutResource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.container.TimeoutHandler;
+
+/**
+ * Asynchronous servlet-deployed resource for testing {@link javax.ws.rs.container.AsyncResponse async response} timeouts.
+ *
+ * @author Michal Gajdos
+ */
+@Path("cancel-timeout")
+public class AsyncCancelTimeoutResource {
+
+    @SuppressWarnings("unchecked")
+    private static final BlockingQueue<AsyncResponse>[] stages = new BlockingQueue[]{
+            new ArrayBlockingQueue<AsyncResponse>(1),
+            new ArrayBlockingQueue<AsyncResponse>(1)
+    };
+
+    @GET
+    @Path("suspend")
+    public void suspend(@Suspended final AsyncResponse asyncResponse) {
+        stages[0].add(asyncResponse);
+    }
+
+    @POST
+    @Path("timeout")
+    public void setTimeout(final String stage) throws Exception {
+        final AsyncResponse async = stages[Integer.parseInt(stage)].take();
+
+        async.setTimeoutHandler(new TimeoutHandler() {
+            @Override
+            public void handleTimeout(final AsyncResponse response) {
+                response.cancel();
+            }
+        });
+        async.setTimeout(200L, TimeUnit.MILLISECONDS);
+
+        stages[Integer.parseInt(stage) + 1].add(async);
+    }
+}
diff --git a/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncResumeTimeoutResource.java b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncResumeTimeoutResource.java
new file mode 100644
index 0000000..e6ad2dc
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncResumeTimeoutResource.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.container.TimeoutHandler;
+
+/**
+ * Asynchronous servlet-deployed resource for testing {@link javax.ws.rs.container.AsyncResponse async response} timeouts.
+ *
+ * @author Michal Gajdos
+ */
+@Path("resume-timeout")
+public class AsyncResumeTimeoutResource {
+
+    private static final BlockingQueue<AsyncResponse> queue = new ArrayBlockingQueue<>(1);
+
+    @GET
+    @Path("suspend")
+    public void suspend(@Suspended final AsyncResponse asyncResponse) {
+        queue.add(asyncResponse);
+    }
+
+    @POST
+    @Path("timeout")
+    public void setTimeOut(final Integer millis) throws InterruptedException {
+        final AsyncResponse asyncResponse = queue.take();
+
+        final boolean timeout1 = asyncResponse.setTimeout(millis, TimeUnit.MILLISECONDS);
+        asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+            @Override
+            public void handleTimeout(final AsyncResponse asyncResponse) {
+                final boolean timeout2 = asyncResponse.setTimeout(millis, TimeUnit.MILLISECONDS);
+                asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+                    @Override
+                    public void handleTimeout(final AsyncResponse asyncResponse) {
+                        asyncResponse.resume("timeout1=" + timeout1 + "_timeout2=" + timeout2 + "_handled");
+                    }
+                });
+            }
+        });
+    }
+}
diff --git a/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResource.java b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResource.java
new file mode 100644
index 0000000..3cd45ec
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+/**
+ * Asynchronous servlet-deployed resource.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("async")
+public class AsyncServletResource {
+    /**
+     * Hello world message.
+     */
+    public static final String HELLO_ASYNC_WORLD = "Hello Async World!";
+    public static final String CANCELED = "Canceled";
+
+    private static BlockingQueue<CanceledRequest> cancelingQueue = new ArrayBlockingQueue<CanceledRequest>(5);
+
+    private static class CanceledRequest {
+        private final String id;
+        private final AsyncResponse asyncResponse;
+
+        private CanceledRequest(final String id, final AsyncResponse asyncResponse) {
+            this.id = id;
+            this.asyncResponse = asyncResponse;
+        }
+    }
+
+    /**
+     * Get the async "Hello World" message.
+     */
+    @GET
+    @Produces("text/plain")
+    public void get(@Suspended final AsyncResponse ar) {
+        Executors.newSingleThreadExecutor().execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(100);
+                    ar.resume(HELLO_ASYNC_WORLD);
+                } catch (final InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+    }
+
+    /**
+     * Get a canceled request.
+     *
+     * @param id request id.
+     * @throws InterruptedException in case of not being able to put the request
+     *                              to an internal queue for canceling.
+     */
+    @GET
+    @Path("canceled")
+    public void getCanceled(@Suspended final AsyncResponse ar, @QueryParam("id") final String id) throws InterruptedException {
+        cancelingQueue.put(new CanceledRequest(id, ar));
+    }
+
+    /**
+     * Cancel a request that is on top of the canceling queue.
+     *
+     * @return notification message about successful request canceling.
+     * @throws InterruptedException in case of not being able to take a cancelled request
+     *                              from an internal canceling queue.
+     */
+    @POST
+    @Produces("text/plain")
+    @Path("canceled")
+    public String cancel(final String requestId) throws InterruptedException {
+        final CanceledRequest canceledRequest = cancelingQueue.take();
+        canceledRequest.asyncResponse.cancel();
+
+        return CANCELED + " " + canceledRequest.id + " by POST " + requestId;
+    }
+
+}
diff --git a/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/Servlet3Async.java b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/Servlet3Async.java
new file mode 100644
index 0000000..c0be482
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_async/Servlet3Async.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_async;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Asynchronous servlet-deployed resource application.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("/")
+public class Servlet3Async extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<>();
+        hashSet.add(AsyncServletResource.class);
+        hashSet.add(AsyncResumeTimeoutResource.class);
+        hashSet.add(AsyncCancelTimeoutResource.class);
+        return hashSet;
+    }
+}
diff --git a/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncCancelTimeoutResourceTest.java b/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncCancelTimeoutResourceTest.java
new file mode 100644
index 0000000..5735317
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncCancelTimeoutResourceTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.Future;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.ServletDeploymentContext;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Asynchronous servlet-deployed resource for testing {@link javax.ws.rs.container.AsyncResponse async response} timeouts.
+ *
+ * @author Michal Gajdos
+ */
+public class AsyncCancelTimeoutResourceTest extends JerseyTest {
+
+    public AsyncCancelTimeoutResourceTest() {
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        return ServletDeploymentContext.builder(new Application())
+                .contextPath("servlet-3-gf-async")
+                .build();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testTimeout() throws Exception {
+        final WebTarget resourceTarget = target("cancel-timeout");
+        final Future<Response> suspend = resourceTarget.path("suspend").request().async().get();
+        final Future<Response> timeout = resourceTarget.path("timeout").request().async().post(Entity.text(0));
+
+        assertThat(timeout.get().getStatus(), is(Response.Status.NO_CONTENT.getStatusCode()));
+        assertThat(suspend.get().getStatus(), is(Response.Status.SERVICE_UNAVAILABLE.getStatusCode()));
+    }
+}
diff --git a/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncResumeTimeoutResourceTest.java b/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncResumeTimeoutResourceTest.java
new file mode 100644
index 0000000..a40976a
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncResumeTimeoutResourceTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_async;
+
+import java.util.concurrent.Future;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.ServletDeploymentContext;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Asynchronous servlet-deployed resource for testing {@link javax.ws.rs.container.AsyncResponse async response} timeouts.
+ *
+ * @author Michal Gajdos
+ */
+public class AsyncResumeTimeoutResourceTest extends JerseyTest {
+
+    public AsyncResumeTimeoutResourceTest() {
+        enable(TestProperties.LOG_TRAFFIC);
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        return ServletDeploymentContext.builder(new Application())
+                .contextPath("servlet-3-gf-async")
+                .build();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testTimeout() throws Exception {
+        final WebTarget resourceTarget = target("resume-timeout");
+        final Future<Response> responseFuture = resourceTarget.path("suspend").request().async().get();
+
+        // Set timeout.
+        assertThat(resourceTarget.path("timeout").request().post(Entity.text("500")).getStatus(), equalTo(204));
+
+        // Wait for it.
+        assertThat(responseFuture.get().readEntity(String.class), equalTo("timeout1=true_timeout2=true_handled"));
+    }
+}
diff --git a/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResourceTest.java b/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResourceTest.java
new file mode 100644
index 0000000..69ac18f
--- /dev/null
+++ b/tests/integration/servlet-3-gf-async/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_async/AsyncServletResourceTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_async;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.ServletDeploymentContext;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Asynchronous servlet-deployed resource test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+public class AsyncServletResourceTest extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(AsyncServletResourceTest.class.getName());
+
+    private static class ResponseRecord {
+
+        final int status;
+        final String message;
+
+        private ResponseRecord(final int status, final String message) {
+            this.status = status;
+            this.message = message;
+        }
+
+        @Override
+        public String toString() {
+            return status + " : \"" + message + '\"';
+        }
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        return ServletDeploymentContext.builder(new Application())
+                .contextPath("servlet-3-gf-async")
+                .build();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    /**
+     * Test asynchronous servlet-deployed resource.
+     *
+     * @throws InterruptedException in case the waiting for all requests to complete was interrupted.
+     */
+    @Test
+    public void testAsyncServlet() throws InterruptedException {
+        final WebTarget resourceTarget = target("async");
+        resourceTarget.register(LoggingFeature.class);
+        final String expectedResponse = AsyncServletResource.HELLO_ASYNC_WORLD;
+
+        final int MAX_MESSAGES = 50;
+        final int LATCH_WAIT_TIMEOUT = 10;
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final Object sequentialGetLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-resource-test-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, ResponseRecord> getResponses = new ConcurrentHashMap<>();
+
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        //noinspection PointlessBooleanExpression,ConstantConditions
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            final Response response = resourceTarget.request().get();
+                            getResponses.put(requestId, new ResponseRecord(response.getStatus(),
+                                    response.readEntity(String.class)));
+                        } catch (final Throwable t) {
+                            t.printStackTrace();
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            //noinspection ConstantConditions
+            if (debugMode) {
+                getRequestLatch.await();
+            } else {
+                assertTrue("Waiting for all GET requests to complete has timed out.",
+                        getRequestLatch.await(LATCH_WAIT_TIMEOUT * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        final StringBuilder messageBuilder = new StringBuilder();
+        for (final Map.Entry<Integer, ResponseRecord> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append("GET response for message ").append(getResponseEntry.getKey()).append(": ")
+                    .append(getResponseEntry.getValue().toString()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        assertEquals(MAX_MESSAGES, getResponses.size());
+        for (final Map.Entry<Integer, ResponseRecord> entry : getResponses.entrySet()) {
+            assertEquals("Unexpected GET response status for request " + entry.getKey(), 200, entry.getValue().status);
+            assertEquals("Unexpected GET response message for request " + entry.getKey(), expectedResponse,
+                    entry.getValue().message);
+        }
+    }
+
+    /**
+     * Test canceling of an async request to a servlet-deployed resource.
+     *
+     * @throws InterruptedException in case the waiting for all requests to complete was interrupted.
+     */
+    @Test
+    public void testAsyncRequestCanceling() throws InterruptedException {
+        final WebTarget resourceTarget = target("async/canceled");
+        resourceTarget.register(LoggingFeature.class);
+
+        final int MAX_MESSAGES = 10;
+        final int LATCH_WAIT_TIMEOUT = 10;
+        final boolean debugMode = false;
+        final boolean sequentialGet = false;
+        final boolean sequentialPost = false;
+        final Object sequentialGetLock = new Object();
+        final Object sequentialPostLock = new Object();
+
+        final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+                .setNameFormat("async-canceled-resource-test-%d")
+                .setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler())
+                .build());
+
+        final Map<Integer, String> postResponses = new ConcurrentHashMap<>();
+        final Map<Integer, String> getResponses = new ConcurrentHashMap<>();
+
+        final CountDownLatch postRequestLatch = new CountDownLatch(MAX_MESSAGES);
+        final CountDownLatch getRequestLatch = new CountDownLatch(MAX_MESSAGES);
+
+        try {
+            for (int i = 0; i < MAX_MESSAGES; i++) {
+                final int requestId = i;
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        //noinspection PointlessBooleanExpression,ConstantConditions
+                        if (debugMode || sequentialGet) {
+                            synchronized (sequentialGetLock) {
+                                get();
+                            }
+                        } else {
+                            get();
+                        }
+                    }
+
+                    private void get() {
+                        try {
+                            final String response = resourceTarget.queryParam("id", requestId).request().get(String.class);
+                            getResponses.put(requestId, response);
+                        } catch (final WebApplicationException ex) {
+                            final Response response = ex.getResponse();
+                            getResponses.put(requestId, response.getStatus() + ": " + response.readEntity(String.class));
+                        } finally {
+                            getRequestLatch.countDown();
+                        }
+                    }
+                });
+                executor.submit(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        //noinspection PointlessBooleanExpression,ConstantConditions
+                        if (debugMode || sequentialPost) {
+                            synchronized (sequentialPostLock) {
+                                post();
+                            }
+                        } else {
+                            post();
+                        }
+                    }
+
+                    private void post() throws ProcessingException {
+                        try {
+                            final String response = resourceTarget.request().post(Entity.text("" + requestId), String.class);
+                            postResponses.put(requestId, response);
+                        } finally {
+                            postRequestLatch.countDown();
+                        }
+                    }
+                });
+            }
+
+            //noinspection ConstantConditions
+            if (debugMode) {
+                postRequestLatch.await();
+                getRequestLatch.await();
+            } else {
+                assertTrue("Waiting for all POST requests to complete has timed out.",
+                        postRequestLatch.await(LATCH_WAIT_TIMEOUT * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
+                assertTrue("Waiting for all GET requests to complete has timed out.",
+                        getRequestLatch.await(LATCH_WAIT_TIMEOUT * getAsyncTimeoutMultiplier(),
+                                TimeUnit.SECONDS));
+            }
+        } finally {
+            executor.shutdownNow();
+        }
+
+        final StringBuilder messageBuilder = new StringBuilder();
+        for (final Map.Entry<Integer, String> postResponseEntry : postResponses.entrySet()) {
+            messageBuilder.append("POST response for message ").append(postResponseEntry.getKey()).append(": ")
+                    .append(postResponseEntry.getValue()).append('\n');
+        }
+        messageBuilder.append('\n');
+        for (final Map.Entry<Integer, String> getResponseEntry : getResponses.entrySet()) {
+            messageBuilder.append("GET response for message ").append(getResponseEntry.getKey()).append(": ")
+                    .append(getResponseEntry.getValue()).append('\n');
+        }
+        LOGGER.info(messageBuilder.toString());
+
+        assertEquals(MAX_MESSAGES, postResponses.size());
+        for (final Map.Entry<Integer, String> postResponseEntry : postResponses.entrySet()) {
+            assertTrue("Unexpected POST notification response for message " + postResponseEntry.getKey(),
+                    postResponseEntry.getValue().startsWith(AsyncServletResource.CANCELED));
+        }
+
+        assertEquals(MAX_MESSAGES, getResponses.size());
+        final Collection<Integer> getResponseKeys = getResponses.keySet();
+        for (int i = 0; i < MAX_MESSAGES; i++) {
+            assertTrue("Detected a GET message response loss: " + i, getResponseKeys.contains(i));
+            final String getResponseEntry = getResponses.get(i);
+            assertTrue("Unexpected canceled GET response status for request " + i, getResponseEntry.startsWith("503: "));
+        }
+    }
+}
diff --git a/tests/integration/servlet-3-inflector-1/pom.xml b/tests/integration/servlet-3-inflector-1/pom.xml
new file mode 100644
index 0000000..312ec21
--- /dev/null
+++ b/tests/integration/servlet-3-inflector-1/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-inflector-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-inflector-1</name>
+
+    <description>
+        Servlet integration test - servlet-3-inflector-1 - Check that inflectors in programmatically created resources are
+        properly injected.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/MyApplication.java b/tests/integration/servlet-3-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/MyApplication.java
new file mode 100644
index 0000000..7c7a797
--- /dev/null
+++ b/tests/integration/servlet-3-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/MyApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_inflector_1;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.model.Resource;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class MyApplication extends ResourceConfig {
+
+    public MyApplication() {
+        final Resource.Builder resourceBuilder = Resource.builder("resource");
+
+        resourceBuilder.addChildResource("class")
+                .addMethod("GET")
+                .handledBy(MyInflector.class);
+        resourceBuilder.addChildResource("instance")
+                .addMethod("GET")
+                .handledBy(new MyInflector());
+
+        registerResources(resourceBuilder.build());
+    }
+}
diff --git a/tests/integration/servlet-3-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/MyInflector.java b/tests/integration/servlet-3-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/MyInflector.java
new file mode 100644
index 0000000..7315a7d
--- /dev/null
+++ b/tests/integration/servlet-3-inflector-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/MyInflector.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_inflector_1;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Response;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.glassfish.jersey.process.Inflector;
+
+/**
+ * @author Michal Gajdos
+ */
+public class MyInflector implements Inflector<ContainerRequestContext, Response> {
+
+    @Inject
+    private Provider<HttpServletRequest> requestProvider;
+    @Inject
+    private Provider<HttpServletResponse> responseProvider;
+
+    @Override
+    public Response apply(final ContainerRequestContext requestContext) {
+        final StringBuilder stringBuilder = new StringBuilder();
+
+        // Request provider & request.
+        if (requestProvider != null) {
+            stringBuilder.append("requestProvider_");
+            stringBuilder.append(requestProvider.get() != null ? "request" : null);
+        } else {
+            stringBuilder.append("null_null");
+        }
+
+        stringBuilder.append('_');
+
+        // Response provider & response.
+        if (responseProvider != null) {
+            stringBuilder.append("responseProvider_");
+            stringBuilder.append(responseProvider.get() != null ? "response" : null);
+        } else {
+            stringBuilder.append("null_null");
+        }
+
+        return Response.ok(stringBuilder.toString()).build();
+    }
+}
diff --git a/tests/integration/servlet-3-inflector-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/InflectorInjectionTestITCase.java b/tests/integration/servlet-3-inflector-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/InflectorInjectionTestITCase.java
new file mode 100644
index 0000000..4dff377
--- /dev/null
+++ b/tests/integration/servlet-3-inflector-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_inflector_1/InflectorInjectionTestITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_inflector_1;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class InflectorInjectionTestITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new MyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInflectorClassInjection() throws Exception {
+        _testInflectorInjection("class");
+    }
+
+    @Test
+    public void testInflectorInstanceInjection() throws Exception {
+        _testInflectorInjection("instance");
+    }
+
+    private void _testInflectorInjection(final String path) {
+        final Response response = target("resource").path(path).request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals("requestProvider_request_responseProvider_response", response.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/servlet-3-init-1/pom.xml b/tests/integration/servlet-3-init-1/pom.xml
new file mode 100644
index 0000000..b0a1d04
--- /dev/null
+++ b/tests/integration/servlet-3-init-1/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-1</name>
+
+    <description>Servlet integration test - servlet-3-init-1 - configured via jax-rs application annotated with @javax.ws.rs.ApplicationPath</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/HelloWorldResource.java b/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/HelloWorldResource.java
new file mode 100644
index 0000000..ea4d55f
--- /dev/null
+++ b/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World!";
+    }
+}
diff --git a/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/Servlet3init1.java b/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/Servlet3init1.java
new file mode 100644
index 0000000..13b7c88
--- /dev/null
+++ b/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/Servlet3init1.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_1;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@ApplicationPath("/")
+public class Servlet3init1 extends Application {
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<Class<?>>();
+        hashSet.add(HelloWorldResource.class);
+        return hashSet;
+    }
+}
diff --git a/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/UnreachableResource.java b/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/UnreachableResource.java
new file mode 100644
index 0000000..5251265
--- /dev/null
+++ b/tests/integration/servlet-3-init-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/UnreachableResource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ *
+ * @author Martin Matula
+ */
+@Path("unreachable")
+public class UnreachableResource {
+    @GET
+    public Response get() {
+        return Response.ok().build();
+    }
+}
diff --git a/tests/integration/servlet-3-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/HelloWorldResourceITCase.java b/tests/integration/servlet-3-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..b2edbf7
--- /dev/null
+++ b/tests/integration/servlet-3-init-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_1/HelloWorldResourceITCase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_1;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Martin Matula
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        WebTarget t = target();
+        t.register(LoggingFeature.class);
+        Response r = t.path("helloworld").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("Hello World!", r.readEntity(String.class));
+    }
+
+    @Test
+    public void testUnreachableResource() {
+        Response r = target().path("unreachable").request().get();
+        assertEquals("Managed to reach a resource that is not registered in the application.", 404, r.getStatus());
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/pom.xml b/tests/integration/servlet-3-init-2/pom.xml
new file mode 100644
index 0000000..0b37d39
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-2</name>
+
+    <description>
+        Servlet integration test - servlet-3-init-2 - configured via jax-rs application annotated with @javax.ws.rs.ApplicationPath
+        and providers annotated with @javax.ws.rs.ext.Provider.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.10</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/CustomFeature.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/CustomFeature.java
new file mode 100644
index 0000000..e36c557
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/CustomFeature.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.tests.integration.servlet_3_init_2.ext.Ext1WriterInterceptor;
+import org.glassfish.jersey.tests.integration.servlet_3_init_2.ext.Ext2WriterInterceptor;
+import org.glassfish.jersey.tests.integration.servlet_3_init_2.ext.Ext3WriterInterceptor;
+import org.glassfish.jersey.tests.integration.servlet_3_init_2.ext.Ext4WriterInterceptor;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class CustomFeature implements Feature {
+
+    @Override
+    public boolean configure(final FeatureContext context) {
+        context.register(Ext3WriterInterceptor.class, 1000);
+        context.register(Ext2WriterInterceptor.class, 100);
+        context.register(Ext1WriterInterceptor.INSTANCE, 500);
+        context.register(Ext4WriterInterceptor.INSTANCE, 1);
+        return true;
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/HelloWorldResource.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/HelloWorldResource.java
new file mode 100644
index 0000000..27b3797
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/HelloWorldResource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World!";
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/Servlet3init2.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/Servlet3init2.java
new file mode 100644
index 0000000..011254f
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/Servlet3init2.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class Servlet3init2 extends ResourceConfig {
+
+    public Servlet3init2() {
+        packages(false, Servlet3init2.class.getPackage().getName());
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext1WriterInterceptor.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext1WriterInterceptor.java
new file mode 100644
index 0000000..84e4b05
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext1WriterInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext1WriterInterceptor implements WriterInterceptor {
+
+    public static final Ext1WriterInterceptor INSTANCE = new Ext1WriterInterceptor();
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext1");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext2WriterInterceptor.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext2WriterInterceptor.java
new file mode 100644
index 0000000..2bc9a05
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext2WriterInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext2WriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext2");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext3WriterInterceptor.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext3WriterInterceptor.java
new file mode 100644
index 0000000..802b1bb
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext3WriterInterceptor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext3WriterInterceptor implements WriterInterceptor {
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext3");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext4WriterInterceptor.java b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext4WriterInterceptor.java
new file mode 100644
index 0000000..77bb734
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/ext/Ext4WriterInterceptor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2.ext;
+
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Michal Gajdos
+ */
+@Provider
+public class Ext4WriterInterceptor implements WriterInterceptor {
+
+    public static final Ext4WriterInterceptor INSTANCE = new Ext4WriterInterceptor();
+
+    @Override
+    public void aroundWriteTo(final WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.setEntity(context.getEntity() + "-ext4");
+        context.proceed();
+    }
+}
diff --git a/tests/integration/servlet-3-init-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/HelloWorldResourceITCase.java b/tests/integration/servlet-3-init-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..c0fbdb0
--- /dev/null
+++ b/tests/integration/servlet-3-init-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_2/HelloWorldResourceITCase.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlet_3_init_2;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Martin Matula
+ * @author Michal Gajdos
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        Response r = target().path("helloworld").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("Hello World!-ext4-ext2-ext1-ext3", r.readEntity(String.class));
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-3/pom.xml b/tests/integration/servlet-3-init-3/pom.xml
new file mode 100644
index 0000000..71ea462
--- /dev/null
+++ b/tests/integration/servlet-3-init-3/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-3</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-3</name>
+
+    <description>Servlet integration test - servlet-3-init-3 - both Application.getClasses and Application.getSingletons return an empty collection</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/EmptyApplication.java b/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/EmptyApplication.java
new file mode 100644
index 0000000..e4f3783
--- /dev/null
+++ b/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/EmptyApplication.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_3;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EmptyApplication extends Application {
+}
diff --git a/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/resource/ResourceOne.java b/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/resource/ResourceOne.java
new file mode 100644
index 0000000..a31e39a
--- /dev/null
+++ b/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/resource/ResourceOne.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_3.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("one")
+public class ResourceOne {
+
+    @GET
+    public String get() {
+        return ResourceOne.class.getSimpleName();
+    }
+}
diff --git a/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/resource/ResourceTwo.java b/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/resource/ResourceTwo.java
new file mode 100644
index 0000000..e6ce245
--- /dev/null
+++ b/tests/integration/servlet-3-init-3/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/resource/ResourceTwo.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_3.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("two")
+public class ResourceTwo {
+
+    @GET
+    public String get() {
+        return ResourceTwo.class.getSimpleName();
+    }
+}
diff --git a/tests/integration/servlet-3-init-3/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-init-3/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..c1ed518
--- /dev/null
+++ b/tests/integration/servlet-3-init-3/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_3.EmptyApplication</servlet-name>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_3.EmptyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-init-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/EmptyApplicationTestITCase.java b/tests/integration/servlet-3-init-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/EmptyApplicationTestITCase.java
new file mode 100644
index 0000000..f762576
--- /dev/null
+++ b/tests/integration/servlet-3-init-3/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_3/EmptyApplicationTestITCase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_3;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.servlet_3_init_3.resource.ResourceOne;
+import org.glassfish.jersey.tests.integration.servlet_3_init_3.resource.ResourceTwo;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EmptyApplicationTestITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new EmptyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testResourceOne() throws Exception {
+        final Response response = target("one").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals(ResourceOne.class.getSimpleName(), response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceTwo() throws Exception {
+        final Response response = target("two").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals(ResourceTwo.class.getSimpleName(), response.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/servlet-3-init-4/pom.xml b/tests/integration/servlet-3-init-4/pom.xml
new file mode 100644
index 0000000..470666d
--- /dev/null
+++ b/tests/integration/servlet-3-init-4/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-4</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-4</name>
+
+    <description>
+        Servlet integration test - servlet-3-init-4 - both Application.getClasses and Application.getSingletons return an empty
+        collection, @ApplicationPath present
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/EmptyApplication.java b/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/EmptyApplication.java
new file mode 100644
index 0000000..5a2042a
--- /dev/null
+++ b/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/EmptyApplication.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_4;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Michal Gajdos
+ */
+@ApplicationPath("/")
+public class EmptyApplication extends Application {
+}
diff --git a/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/resource/ResourceOne.java b/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/resource/ResourceOne.java
new file mode 100644
index 0000000..f43e50c
--- /dev/null
+++ b/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/resource/ResourceOne.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_4.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("one")
+public class ResourceOne {
+
+    @GET
+    public String get() {
+        return ResourceOne.class.getSimpleName();
+    }
+}
diff --git a/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/resource/ResourceTwo.java b/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/resource/ResourceTwo.java
new file mode 100644
index 0000000..45e9de4
--- /dev/null
+++ b/tests/integration/servlet-3-init-4/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/resource/ResourceTwo.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_4.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("two")
+public class ResourceTwo {
+
+    @GET
+    public String get() {
+        return ResourceTwo.class.getSimpleName();
+    }
+}
diff --git a/tests/integration/servlet-3-init-4/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/EmptyApplicationTestITCase.java b/tests/integration/servlet-3-init-4/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/EmptyApplicationTestITCase.java
new file mode 100644
index 0000000..4c3b2b6
--- /dev/null
+++ b/tests/integration/servlet-3-init-4/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_4/EmptyApplicationTestITCase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_4;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.servlet_3_init_4.resource.ResourceOne;
+import org.glassfish.jersey.tests.integration.servlet_3_init_4.resource.ResourceTwo;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EmptyApplicationTestITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new EmptyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testResourceOne() throws Exception {
+        final Response response = target("one").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals(ResourceOne.class.getSimpleName(), response.readEntity(String.class));
+    }
+
+    @Test
+    public void testResourceTwo() throws Exception {
+        final Response response = target("two").request().get();
+
+        assertEquals(200, response.getStatus());
+        assertEquals(ResourceTwo.class.getSimpleName(), response.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/servlet-3-init-5/pom.xml b/tests/integration/servlet-3-init-5/pom.xml
new file mode 100644
index 0000000..e96aac6
--- /dev/null
+++ b/tests/integration/servlet-3-init-5/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-5</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-5</name>
+
+    <description>Servlet integration test - servlet-3-init-5 - no Application subclass is present;
+        dynamically add a servlet and set its name to javax.ws.rs.core.Application</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWorldResource.java b/tests/integration/servlet-3-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWorldResource.java
new file mode 100644
index 0000000..91d8093
--- /dev/null
+++ b/tests/integration/servlet-3-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWorldResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_5;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public Hello get() {
+        return new Hello();
+    }
+
+    public static class Hello {
+
+    }
+}
diff --git a/tests/integration/servlet-3-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWriter.java b/tests/integration/servlet-3-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWriter.java
new file mode 100644
index 0000000..d70573a
--- /dev/null
+++ b/tests/integration/servlet-3-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWriter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_5;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+public class HelloWriter implements MessageBodyWriter<HelloWorldResource.Hello> {
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(HelloWorldResource.Hello.class);
+    }
+
+    @Override
+    public long getSize(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
+            throws IOException, WebApplicationException {
+        entityStream.write(("Hello World! " + this.getClass().getPackage().getName())
+                .getBytes(MessageUtils.getCharset(mediaType)));
+    }
+}
diff --git a/tests/integration/servlet-3-init-5/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-init-5/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..520cb3e
--- /dev/null
+++ b/tests/integration/servlet-3-init-5/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <servlet>
+        <servlet-name>javax.ws.rs.core.Application</servlet-name>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>javax.ws.rs.core.Application</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWorldResourceITCase.java b/tests/integration/servlet-3-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..af14376
--- /dev/null
+++ b/tests/integration/servlet-3-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_5/HelloWorldResourceITCase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_5;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-3-init-6/pom.xml b/tests/integration/servlet-3-init-6/pom.xml
new file mode 100644
index 0000000..709c875
--- /dev/null
+++ b/tests/integration/servlet-3-init-6/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-6</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-6</name>
+
+    <description>Servlet integration test - servlet-3-init-5 - Application subclass is present;
+        servlet-mapping in web.xml</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/EmptyApplication.java b/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/EmptyApplication.java
new file mode 100644
index 0000000..760f1c5
--- /dev/null
+++ b/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/EmptyApplication.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_6;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EmptyApplication extends Application {
+}
diff --git a/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/resource/ResourceOne.java b/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/resource/ResourceOne.java
new file mode 100644
index 0000000..a4da8ea
--- /dev/null
+++ b/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/resource/ResourceOne.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_6.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("one")
+public class ResourceOne {
+
+    @GET
+    public String get() {
+        return ResourceOne.class.getSimpleName();
+    }
+}
diff --git a/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/resource/ResourceTwo.java b/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/resource/ResourceTwo.java
new file mode 100644
index 0000000..8d4e270
--- /dev/null
+++ b/tests/integration/servlet-3-init-6/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/resource/ResourceTwo.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_6.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("two")
+public class ResourceTwo {
+
+    @GET
+    public String get() {
+        return ResourceTwo.class.getSimpleName();
+    }
+}
diff --git a/tests/integration/servlet-3-init-6/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-init-6/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d5bf156
--- /dev/null
+++ b/tests/integration/servlet-3-init-6/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+        version="3.0">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_6.EmptyApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_6.EmptyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-init-6/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/EmptyApplicationITCase.java b/tests/integration/servlet-3-init-6/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/EmptyApplicationITCase.java
new file mode 100644
index 0000000..c85d8bb
--- /dev/null
+++ b/tests/integration/servlet-3-init-6/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_6/EmptyApplicationITCase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_6;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.servlet_3_init_6.resource.ResourceOne;
+import org.glassfish.jersey.tests.integration.servlet_3_init_6.resource.ResourceTwo;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class EmptyApplicationITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new EmptyApplication();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testResourceOne() throws Exception {
+        final Response response = target("one").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(ResourceOne.class.getSimpleName()));
+    }
+
+    @Test
+    public void testResourceTwo() throws Exception {
+        final Response response = target("two").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is(ResourceTwo.class.getSimpleName()));
+    }
+}
diff --git a/tests/integration/servlet-3-init-7/pom.xml b/tests/integration/servlet-3-init-7/pom.xml
new file mode 100644
index 0000000..30869d5
--- /dev/null
+++ b/tests/integration/servlet-3-init-7/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-7</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-7</name>
+
+    <description>Servlet integration test - servlet-3-init-7 - no Application subclass is present;
+        dynamically add a servlet and set its name to javax.ws.rs.core.Application;
+        servlet-mapping in web.xml</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWorldResource.java b/tests/integration/servlet-3-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWorldResource.java
new file mode 100644
index 0000000..acaaf92
--- /dev/null
+++ b/tests/integration/servlet-3-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWorldResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_7;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld")
+public class HelloWorldResource {
+
+    @GET
+    @Produces("text/plain")
+    public Hello get() {
+        return new Hello();
+    }
+
+    public static class Hello {
+
+    }
+}
diff --git a/tests/integration/servlet-3-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWriter.java b/tests/integration/servlet-3-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWriter.java
new file mode 100644
index 0000000..3e9898f
--- /dev/null
+++ b/tests/integration/servlet-3-init-7/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWriter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_7;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+public class HelloWriter implements MessageBodyWriter<HelloWorldResource.Hello> {
+
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(HelloWorldResource.Hello.class);
+    }
+
+    @Override
+    public long getSize(final HelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final HelloWorldResource.Hello hello,
+                        final Class<?> type,
+                        final Type genericType,
+                        final Annotation[] annotations,
+                        final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        entityStream.write(("Hello World! " + this.getClass().getPackage().getName()).getBytes());
+    }
+}
diff --git a/tests/integration/servlet-3-init-7/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-init-7/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..c87f441
--- /dev/null
+++ b/tests/integration/servlet-3-init-7/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <servlet>
+        <servlet-name>javax.ws.rs.core.Application</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>javax.ws.rs.core.Application</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-init-7/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWorldResourceITCase.java b/tests/integration/servlet-3-init-7/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..43f36e3
--- /dev/null
+++ b/tests/integration/servlet-3-init-7/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_7/HelloWorldResourceITCase.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, 2018 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.integration.servlet_3_init_7;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(HelloWorldResource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        String s = target().path("helloworld").request().get(String.class);
+        assertTrue(s.equals("Hello World! " + this.getClass().getPackage().getName()));
+    }
+}
diff --git a/tests/integration/servlet-3-init-8/pom.xml b/tests/integration/servlet-3-init-8/pom.xml
new file mode 100644
index 0000000..094fcfa
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-8</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-8</name>
+
+    <description>Servlet integration test - servlet-3-init-8 - more jax-rs applications
+        (annotated with @javax.ws.rs.ApplicationPath and/or configured in web.xml).
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld1Resource.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld1Resource.java
new file mode 100644
index 0000000..5604139
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld1Resource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld1")
+public class HelloWorld1Resource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World 1!";
+    }
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld2Resource.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld2Resource.java
new file mode 100644
index 0000000..cba1bdd
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld2Resource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld2")
+public class HelloWorld2Resource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World 2!";
+    }
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld3Resource.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld3Resource.java
new file mode 100644
index 0000000..010ae62
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld3Resource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld3")
+public class HelloWorld3Resource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World 3!";
+    }
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld4Resource.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld4Resource.java
new file mode 100644
index 0000000..82936a1
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorld4Resource.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld4")
+public class HelloWorld4Resource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello World 4!";
+    }
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App1.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App1.java
new file mode 100644
index 0000000..e32c718
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App1.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * The application is fully configured in {@code web.xml} ({@code servlet} as well as {@code servlet-mapping}).
+ * It means {@code ApplicationPath.value} ({@code /app1ann}) is overridden using a {@code servlet-mapping} element.
+ * The application also explicitly register JAX-RS resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/app1ann")
+public class Servlet3Init8App1 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<>();
+        hashSet.add(HelloWorld1Resource.class);
+        return hashSet;
+    }
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App2.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App2.java
new file mode 100644
index 0000000..1ca6ecf
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App2.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * The application is partially configured in {@code web.xml} (just {@code servlet} element).
+ * It means {@code ApplicationPath.value} ({@code /app2ann}) is used as base servlet URI.
+ * The application also explicitly register JAX-RS resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/app2ann")
+public class Servlet3Init8App2 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<>();
+        hashSet.add(HelloWorld2Resource.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App3.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App3.java
new file mode 100644
index 0000000..c7163e1
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App3.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * The application is not configured in {@code web.xml}.
+ * It means {@code ApplicationPath.value} ({@code /app3ann}) is used as base servlet URI.
+ * The application also explicitly register JAX-RS resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/app3ann")
+public class Servlet3Init8App3 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<>();
+        hashSet.add(HelloWorld3Resource.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App4.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App4.java
new file mode 100644
index 0000000..9a3667c
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App4.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * The application is fully configured in {@code web.xml} ({@code servlet} as well as {@code servlet-mapping}).
+ * And there is no {@code ApplicationPath} annotation on the class.
+ * The application also explicitly register JAX-RS resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Servlet3Init8App4 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<>();
+        hashSet.add(HelloWorld4Resource.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App5.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App5.java
new file mode 100644
index 0000000..b1f2ea8
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/Servlet3Init8App5.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * The application is fully configured in {@code web.xml} ({@code servlet} as well as {@code servlet-mapping}).
+ * And there is no {@code ApplicationPath} annotation on the class.
+ * The application does NOT explicitly register any JAX-RS resource. All available resources are automatically registered.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Servlet3Init8App5 extends Application {
+
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/UnreachableResource.java b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/UnreachableResource.java
new file mode 100644
index 0000000..99f9710
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/UnreachableResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("unreachable")
+public class UnreachableResource {
+
+    @GET
+    @Produces("text/plain")
+    public String get() {
+        return "Hello Unreachable!";
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-8/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-init-8/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..d5c3a63
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App1</servlet-name>
+        <url-pattern>/app1/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App2</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    </servlet>
+
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App4</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App4</servlet-name>
+        <url-pattern>/app4/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App5</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_8.Servlet3Init8App5</servlet-name>
+        <url-pattern>/app5/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorldResourceITCase.java b/tests/integration/servlet-3-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorldResourceITCase.java
new file mode 100644
index 0000000..0955681
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorldResourceITCase.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test reachable resources.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class HelloWorldResourceITCase extends JerseyTest {
+
+    private final String appPath;
+    private final String resourcePath;
+    private final String helloName;
+
+    public HelloWorldResourceITCase(String appPath, String resourcePath, String helloName) {
+        this.appPath = appPath;
+        this.resourcePath = resourcePath;
+        this.helloName = helloName;
+    }
+
+    @Parameterized.Parameters(name = "{index}: {0}/{1} \"{2}\"")
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"/app1", "helloworld1", "World 1"}, //app1 - overridden using a servlet-mapping element in the web.xml
+                {"/app2ann", "helloworld2", "World 2"}, //app2ann - no servlet-mapping in the web.xml, used ApplicationPath.value
+                {"/app3ann", "helloworld3", "World 3"}, //app3ann - no servlet in the web.xml, used ApplicationPath.value
+                {"/app4", "helloworld4", "World 4"}, //app4 - fully configured in web.xml
+                //app5 -  automatic registration of all resources, no explicit classes/singletons provided by Servlet3Init8App5
+                {"/app5", "helloworld1", "World 1"},
+                {"/app5", "helloworld2", "World 2"},
+                {"/app5", "helloworld3", "World 3"},
+                {"/app5", "helloworld4", "World 4"},
+                {"/app5", "unreachable", "Unreachable"},
+        });
+    }
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        WebTarget t = target(appPath);
+        t.register(LoggingFeature.class);
+        Response r = t.path(resourcePath).request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("Hello " + helloName + "!", r.readEntity(String.class));
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorldResourceUnreachableITCase.java b/tests/integration/servlet-3-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorldResourceUnreachableITCase.java
new file mode 100644
index 0000000..914687d
--- /dev/null
+++ b/tests/integration/servlet-3-init-8/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_8/HelloWorldResourceUnreachableITCase.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_3_init_8;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test unreachable resources.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class HelloWorldResourceUnreachableITCase extends JerseyTest {
+
+    private final String appPath;
+    private final String resourcePath;
+
+    public HelloWorldResourceUnreachableITCase(String appPath,
+                                               String resourcePath) {
+        this.appPath = appPath;
+        this.resourcePath = resourcePath;
+    }
+
+    @Parameterized.Parameters(name = "{index}: {0}/{1}")
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"/app1", "unreachable"}, //unreachable - no explicitly mentioned resource
+                {"/app1ann", "helloworld1"}, //app1ann - overridden using a servlet-mapping element in the web.xml
+                {"/app2ann", "unreachable"}, //unreachable - no explicitly mentioned resource
+                {"/app3ann", "unreachable"}, //unreachable - no explicitly mentioned resource
+                {"/app4", "unreachable"}, //unreachable - no explicitly mentioned resource
+        });
+    }
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testUnreachableResource() {
+        WebTarget t = target(appPath);
+        t.register(LoggingFeature.class);
+        Response r = t.path(resourcePath).request().get();
+        assertEquals(404, r.getStatus());
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/pom.xml b/tests/integration/servlet-3-init-provider/pom.xml
new file mode 100644
index 0000000..75417cb
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-init-provider</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-init-provider</name>
+
+    <description>Servlet integration test - servlet-3-init-provider - ServletContainerProviderFactory, ServletContainerProvider</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-servlet-portability</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/AbstractHelloWorldResource.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/AbstractHelloWorldResource.java
new file mode 100644
index 0000000..489a50b
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/AbstractHelloWorldResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public abstract class AbstractHelloWorldResource {
+
+    public static final int NUMBER_OF_APPLICATIONS = 5;
+
+    @GET
+    @Produces("text/plain")
+    public Hello get() {
+        return new Hello(createName());
+    }
+
+    protected abstract String createName();
+
+
+    public static class Hello {
+        private final String name;
+
+        public Hello(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application1.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application1.java
new file mode 100644
index 0000000..8316b40
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application1.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Application1 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<Class<?>>();
+        hashSet.add(HelloWorld1Resource.class);
+        hashSet.add(HelloWriter.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application2.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application2.java
new file mode 100644
index 0000000..89f1683
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application2.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Application2 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<Class<?>>();
+        hashSet.add(HelloWorld2Resource.class);
+        hashSet.add(HelloWriter.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application3.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application3.java
new file mode 100644
index 0000000..20932c5
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application3.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Application3 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<Class<?>>();
+        hashSet.add(HelloWorld3Resource.class);
+        hashSet.add(HelloWriter.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application4.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application4.java
new file mode 100644
index 0000000..e89767d
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/Application4.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/application4/*")
+public class Application4 extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        final Set<Class<?>> hashSet = new HashSet<Class<?>>();
+        hashSet.add(HelloWorld4Resource.class);
+        hashSet.add(HelloWriter.class);
+        return hashSet;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld1Resource.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld1Resource.java
new file mode 100644
index 0000000..9ffd315
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld1Resource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld1")
+public class HelloWorld1Resource extends AbstractHelloWorldResource {
+
+    @Override
+    protected String createName() {
+        return "World #1";
+    }
+
+    @GET
+    @Path("servlets")
+    public int getServletsCount() {
+        return TestServletContainerProvider.getServletNames().size();
+    }
+
+    @GET
+    @Path("servlets/{name}")
+    public boolean hasServletName(@PathParam("name") String servletName) {
+        return TestServletContainerProvider.getServletNames().contains(servletName);
+    }
+
+    @GET
+    @Path("immutableServletNames")
+    public boolean isImmutableServletNames() {
+        return TestServletContainerProvider.isImmutableServletNames();
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld2Resource.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld2Resource.java
new file mode 100644
index 0000000..99cc572
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld2Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.ws.rs.Path;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld2")
+public class HelloWorld2Resource extends AbstractHelloWorldResource {
+
+    @Override
+    protected String createName() {
+        return "World #2";
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld3Resource.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld3Resource.java
new file mode 100644
index 0000000..9f510e0
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld3Resource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld3")
+public class HelloWorld3Resource extends AbstractHelloWorldResource {
+
+    @Override
+    protected String createName() {
+        return "World #3";
+    }
+
+    @GET
+    @Path("containers")
+    public int getContainersCount() throws InterruptedException {
+        return TestContainerLifecycleListener.getStartupCount();
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld4Resource.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld4Resource.java
new file mode 100644
index 0000000..f8db2e3
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld4Resource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.ws.rs.Path;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld4")
+public class HelloWorld4Resource extends AbstractHelloWorldResource {
+
+    @Override
+    protected String createName() {
+        return "World #4";
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld5Resource.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld5Resource.java
new file mode 100644
index 0000000..aa8350a
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld5Resource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import java.util.Enumeration;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("helloworld5")
+public class HelloWorld5Resource extends AbstractHelloWorldResource {
+
+    @Context
+    private HttpServletRequest request;
+
+    @Override
+    protected String createName() {
+        return "World #5";
+    }
+
+    @GET
+    @Path("filter")
+    public String getFilter() {
+        return (String) request.getAttribute("FILTER");
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWriter.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWriter.java
new file mode 100644
index 0000000..cb061cd
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWriter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.message.MessageUtils;
+
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+public class HelloWriter implements MessageBodyWriter<AbstractHelloWorldResource.Hello> {
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(AbstractHelloWorldResource.Hello.class);
+    }
+
+    @Override
+    public long getSize(final AbstractHelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final AbstractHelloWorldResource.Hello hello, final Class<?> type, final Type genericType,
+                        final Annotation[] annotations, final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        final String value = String.format("Hello %s!", hello.getName());
+        entityStream.write(value.getBytes(MessageUtils.getCharset(mediaType)));
+    }
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestContainerLifecycleListener.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestContainerLifecycleListener.java
new file mode 100644
index 0000000..e8a1b0e
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestContainerLifecycleListener.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
+
+/**
+ * This is just test purpose implementation of Jersey SPI {@link ContainerLifecycleListener}.
+ * The listener class is registered in {@link TestServletContainerProvider} to {@link ResourceConfig}.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TestContainerLifecycleListener implements ContainerLifecycleListener {
+
+    private static int startupCount = 0;
+
+    private static CountDownLatch latch = new CountDownLatch(AbstractHelloWorldResource.NUMBER_OF_APPLICATIONS);
+
+    @Override
+    public void onStartup(Container container) {
+        latch.countDown();
+        startupCount++;
+    }
+
+    @Override
+    public void onReload(Container container) {
+    }
+
+    @Override
+    public void onShutdown(Container container) {
+    }
+
+    public static int getStartupCount() throws InterruptedException {
+        latch.await(5, TimeUnit.SECONDS);
+        return startupCount;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestFilter.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestFilter.java
new file mode 100644
index 0000000..27093c3
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestFilter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * This is just test purpose {@link Filter servlet filter} implementation to demonstrate how to register filter for any Jersey
+ * Servlet.
+ * The filter class is added in {@link TestServletContainerProvider}.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TestFilter implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException {
+        if (((HttpServletRequest) request).getRequestURI().startsWith("/application5")) {
+            request.setAttribute("FILTER", TestServletContainerProvider.TEST_FILTER);
+        }
+
+        chain.doFilter(request, response);
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestServletContainerProvider.java b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestServletContainerProvider.java
new file mode 100644
index 0000000..abe1a50
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/TestServletContainerProvider.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.servlet.internal.spi.RequestScopedInitializerProvider;
+import org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider;
+
+/**
+ * This is just test purpose implementation of Jersey internal SPI {@link ServletContainerProvider}.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TestServletContainerProvider implements ServletContainerProvider {
+
+    public static final String TEST_FILTER = "TestFilter";
+
+    private static Set<String> SERVLET_NAMES;
+    private static boolean immutableServletNames = false;
+
+    @Override
+    public void preInit(final ServletContext servletContext, final Set<Class<?>> classes) throws ServletException {
+        classes.add(AbstractHelloWorldResource.class);
+    }
+
+    @Override
+    public void postInit(final ServletContext servletContext, final Set<Class<?>> classes, final Set<String> servletNames)
+            throws ServletException {
+        try {
+            servletNames.add("TEST");
+        } catch (final UnsupportedOperationException ex) {
+            TestServletContainerProvider.setImmutableServletNames(true);
+        }
+    }
+
+    @Override
+    public void onRegister(final ServletContext servletContext, final Set<String> servletNames) throws ServletException {
+        TestServletContainerProvider.setServletNames(servletNames);
+
+        servletContext.addFilter(TEST_FILTER, TestFilter.class)
+                .addMappingForServletNames(EnumSet.allOf(DispatcherType.class), false,
+                        servletNames.toArray(new String[servletNames.size()]));
+    }
+
+    @Override
+    public void configure(final ResourceConfig resourceConfig) throws ServletException {
+        if (!resourceConfig.isRegistered(TestContainerLifecycleListener.class)) {
+            resourceConfig.register(TestContainerLifecycleListener.class);
+        }
+    }
+
+    public static Set<String> getServletNames() {
+        return SERVLET_NAMES;
+    }
+
+    public static boolean isImmutableServletNames() {
+        return immutableServletNames;
+    }
+
+    private static void setServletNames(final Set<String> servletNames) {
+        TestServletContainerProvider.SERVLET_NAMES = servletNames;
+    }
+
+    public static void setImmutableServletNames(final boolean immutableServletNames) {
+        TestServletContainerProvider.immutableServletNames = immutableServletNames;
+    }
+}
diff --git a/tests/integration/servlet-3-init-provider/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider b/tests/integration/servlet-3-init-provider/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
new file mode 100644
index 0000000..14fedce
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.integration.servlet_3_init_provider.TestServletContainerProvider
\ No newline at end of file
diff --git a/tests/integration/servlet-3-init-provider/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-init-provider/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..aa1f9ea
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <servlet>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_provider.Application1</servlet-name>
+    </servlet>
+    <servlet>
+        <servlet-name>application2</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_3_init_provider.Application2</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet>
+        <servlet-name>application3</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.portability.PortableServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey2#javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_3_init_provider.Application3</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet>
+        <servlet-name>javax.ws.rs.core.Application</servlet-name>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_3_init_provider.HelloWorld5Resource,
+                org.glassfish.jersey.tests.integration.servlet_3_init_provider.HelloWriter
+            </param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.tests.integration.servlet_3_init_provider.Application1</servlet-name>
+        <url-pattern>/application1/*</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>application2</servlet-name>
+        <url-pattern>/application2/*</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>application3</servlet-name>
+        <url-pattern>/application3/*</url-pattern>
+    </servlet-mapping>
+    <servlet-mapping>
+        <servlet-name>javax.ws.rs.core.Application</servlet-name>
+        <url-pattern>/application5/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/AbstractHelloWorldResourceTest.java b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/AbstractHelloWorldResourceTest.java
new file mode 100644
index 0000000..fec1830
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/AbstractHelloWorldResourceTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import org.junit.Assert;
+
+import javax.ws.rs.NotFoundException;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public abstract class AbstractHelloWorldResourceTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new ResourceConfig(getResourceClass());
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        for (int i = 1; i <= AbstractHelloWorldResource.NUMBER_OF_APPLICATIONS; i++) {
+            try {
+                String actual = target("application" + getIndex()).path("helloworld" + i).request().get(String.class);
+                if (i == getIndex()) {
+                    Assert.assertEquals("Hello World #" + getIndex() + "!", actual);
+                } else {
+                    Assert.fail("i: " + i + " | [" + actual + "]");
+                }
+            } catch (NotFoundException ex) {
+                if (i != getIndex()) {
+                    Assert.assertEquals(404, ex.getResponse().getStatus());
+                } else {
+                    Assert.fail("!!! i: " + i);
+                }
+            }
+        }
+    }
+
+    protected abstract Class<?> getResourceClass();
+
+    protected abstract int getIndex();
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld1ResourceITCase.java b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld1ResourceITCase.java
new file mode 100644
index 0000000..1299390
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld1ResourceITCase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorld1ResourceITCase extends AbstractHelloWorldResourceTest {
+
+    protected Class<?> getResourceClass() {
+        return HelloWorld1Resource.class;
+    }
+
+    protected int getIndex() {
+        return 1;
+    }
+
+    @Test
+    public void testRegisteredServletNames() throws Exception {
+        WebTarget target = target("application" + getIndex()).path("helloworld" + getIndex()).path("servlets");
+        Assert.assertEquals(AbstractHelloWorldResource.NUMBER_OF_APPLICATIONS, (int) target.request().get(Integer.TYPE));
+
+        target = target.path("{name}");
+        testRegisteredServletNames(target, "org.glassfish.jersey.tests.integration.servlet_3_init_provider.Application1");
+        testRegisteredServletNames(target, "application2");
+        testRegisteredServletNames(target, "application3");
+        testRegisteredServletNames(target, "org.glassfish.jersey.tests.integration.servlet_3_init_provider.Application4");
+        testRegisteredServletNames(target, "javax.ws.rs.core.Application");
+    }
+
+    private void testRegisteredServletNames(WebTarget target, String servletName) throws Exception {
+        Assert.assertTrue(target.resolveTemplate("name", servletName).request().get(Boolean.TYPE));
+    }
+
+    @Test
+    public void testImmutableServletNames() {
+        WebTarget target = target("application" + getIndex()).path("helloworld" + getIndex()).path("immutableServletNames");
+        Assert.assertTrue(target.request().get(Boolean.TYPE));
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld2ResourceITCase.java b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld2ResourceITCase.java
new file mode 100644
index 0000000..e38ac59
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld2ResourceITCase.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorld2ResourceITCase extends AbstractHelloWorldResourceTest {
+
+    protected Class<?> getResourceClass() {
+        return HelloWorld2Resource.class;
+    }
+
+    protected int getIndex() {
+        return 2;
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld3ResourceITCase.java b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld3ResourceITCase.java
new file mode 100644
index 0000000..6a16e46
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld3ResourceITCase.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.ws.rs.client.WebTarget;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorld3ResourceITCase extends AbstractHelloWorldResourceTest {
+
+    protected Class<?> getResourceClass() {
+        return HelloWorld3Resource.class;
+    }
+
+    protected int getIndex() {
+        return 3;
+    }
+
+    @Test
+    public void testStartupContainers() throws Exception {
+        WebTarget target = target("application" + getIndex()).path("helloworld" + getIndex()).path("containers");
+        Assert.assertEquals(AbstractHelloWorldResource.NUMBER_OF_APPLICATIONS, (int) target.request().get(Integer.TYPE));
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld4ResourceITCase.java b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld4ResourceITCase.java
new file mode 100644
index 0000000..9d39e25
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld4ResourceITCase.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorld4ResourceITCase extends AbstractHelloWorldResourceTest {
+
+    protected Class<?> getResourceClass() {
+        return HelloWorld4Resource.class;
+    }
+
+    protected int getIndex() {
+        return 4;
+    }
+
+    @Test
+    public void testRegisterFilter() throws Exception {
+        Response response = target("application" + getIndex()).path("helloworld" + getIndex()).path("filter").request().get();
+        Assert.assertEquals(404, response.getStatus());
+    }
+
+}
diff --git a/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld5ResourceITCase.java b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld5ResourceITCase.java
new file mode 100644
index 0000000..81dae7b
--- /dev/null
+++ b/tests/integration/servlet-3-init-provider/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_init_provider/HelloWorld5ResourceITCase.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_init_provider;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class HelloWorld5ResourceITCase extends AbstractHelloWorldResourceTest {
+
+    protected Class<?> getResourceClass() {
+        return HelloWorld5Resource.class;
+    }
+
+    protected int getIndex() {
+        return 5;
+    }
+
+    @Test
+    public void testRegisterFilter() throws Exception {
+        String actual = target("application" + getIndex()).path("helloworld" + getIndex()).path("filter").request()
+                .get(String.class);
+        Assert.assertEquals(TestServletContainerProvider.TEST_FILTER, actual);
+    }
+
+}
diff --git a/tests/integration/servlet-3-params/pom.xml b/tests/integration/servlet-3-params/pom.xml
new file mode 100644
index 0000000..c0a5ceb
--- /dev/null
+++ b/tests/integration/servlet-3-params/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-params</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-params</name>
+
+    <description>Servlet integration test - servlet-3-params</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-params/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_params/CustomContextListener.java b/tests/integration/servlet-3-params/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_params/CustomContextListener.java
new file mode 100644
index 0000000..82a54c8
--- /dev/null
+++ b/tests/integration/servlet-3-params/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_params/CustomContextListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_params;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@WebListener
+public class CustomContextListener implements ServletContextListener {
+
+    @Override
+    public void contextInitialized(ServletContextEvent servletContextEvent) {
+        servletContextEvent.getServletContext().setAttribute("myContextParam", "myValue");
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent servletContextEvent) {
+
+    }
+}
+
diff --git a/tests/integration/servlet-3-params/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_params/ParamResource.java b/tests/integration/servlet-3-params/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_params/ParamResource.java
new file mode 100644
index 0000000..79ef7a8
--- /dev/null
+++ b/tests/integration/servlet-3-params/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_params/ParamResource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_params;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+@Path("params")
+public class ParamResource {
+
+    @Context
+    Application application;
+
+    @GET
+    @Produces("text/plain")
+    public String get(@QueryParam("param-name") String paramName) {
+        return application.getProperties().get(paramName).toString();
+    }
+}
diff --git a/tests/integration/servlet-3-params/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-3-params/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1f118a7
--- /dev/null
+++ b/tests/integration/servlet-3-params/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+    <servlet>
+        <servlet-name>testServlet1</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_3_params.ParamResource</param-value>
+        </init-param>
+        <init-param>
+            <param-name>myInitParam</param-name>
+            <param-value>myValue</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet1</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-3-params/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_params/ParamsTestITCase.java b/tests/integration/servlet-3-params/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_params/ParamsTestITCase.java
new file mode 100644
index 0000000..5dc4151
--- /dev/null
+++ b/tests/integration/servlet-3-params/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_params/ParamsTestITCase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_params;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Pavel Bucek (pavel.bucek at oracle.com)
+ */
+public class ParamsTestITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    // see web.xml
+    @Test
+    public void testInitParam() throws Exception {
+        Response r = target().path("params").queryParam("param-name", "myInitParam").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("myValue", r.readEntity(String.class));
+    }
+
+    // see CustomContextListener.java
+    @Test
+    public void testContextParam() throws Exception {
+        Response r = target().path("params").queryParam("param-name", "myContextParam").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("myValue", r.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/servlet-3-sse-1/pom.xml b/tests/integration/servlet-3-sse-1/pom.xml
new file mode 100644
index 0000000..d7ea408
--- /dev/null
+++ b/tests/integration/servlet-3-sse-1/pom.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-3-sse-1</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-3-sse-1</name>
+
+    <description>Servlet integration test - servlet-3-sse-1</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <!--plugin>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <scanIntervalSeconds>0</scanIntervalSeconds>
+                    <stopPort>9999</stopPort>
+                    <stopKey>STOP</stopKey>
+                    <contextPath>/</contextPath>
+                    <webApp>
+                        <contextPath>/</contextPath>
+                    </webApp>
+                    <war>${project.build.directory}/${project.build.finalName}.war</war>
+                    <systemProperties>
+                        <systemProperty>
+                            <name>jetty.port</name>
+                            <value>${jersey.config.test.container.port}</value>
+                        </systemProperty>
+                    </systemProperties>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>start-jetty</id>
+                        <phase>pre-integration-test</phase>
+                        <goals>
+                            <goal>run-war</goal>
+                        </goals>
+                        <configuration>
+                            <daemon>true</daemon>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>stop-jetty</id>
+                        <phase>post-integration-test</phase>
+                        <goals>
+                            <goal>stop</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin-->
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/servlet-3-sse-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreApp.java b/tests/integration/servlet-3-sse-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreApp.java
new file mode 100644
index 0000000..3e37025
--- /dev/null
+++ b/tests/integration/servlet-3-sse-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreApp.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_sse_1;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * SSE item store JAX-RS application class.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@ApplicationPath("resources")
+public class ItemStoreApp extends ResourceConfig {
+    /**
+     * Create new SSE Item Store Example JAX-RS application.
+     */
+    public ItemStoreApp() {
+        super(ItemStoreResource.class, SseFeature.class);
+    }
+}
diff --git a/tests/integration/servlet-3-sse-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreResource.java b/tests/integration/servlet-3-sse-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreResource.java
new file mode 100644
index 0000000..dbbb285
--- /dev/null
+++ b/tests/integration/servlet-3-sse-1/src/main/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreResource.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_sse_1;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Logger;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.ServiceUnavailableException;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseBroadcaster;
+import org.glassfish.jersey.media.sse.SseFeature;
+
+/**
+ * A resource for storing named items.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Path("items")
+public class ItemStoreResource {
+    private static final Logger LOGGER = Logger.getLogger(ItemStoreResource.class.getName());
+
+    private static final ReentrantReadWriteLock storeLock = new ReentrantReadWriteLock();
+    private static final LinkedList<String> itemStore = new LinkedList<String>();
+    private static final SseBroadcaster broadcaster = new SseBroadcaster();
+
+    private static volatile long reconnectDelay = 0;
+
+    /**
+     * List all stored items.
+     *
+     * @return list of all stored items.
+     */
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String listItems() {
+        try {
+            storeLock.readLock().lock();
+            return itemStore.toString();
+        } finally {
+            storeLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Receive & process commands sent by the test client that control the internal resource state.
+     *
+     * Following is the list of recognized commands:
+     * <ul>
+     * <li><b>disconnect</b> - disconnect all registered event streams.</li>
+     * <li><b>reconnect now</b> - enable client reconnecting.</li>
+     * <li><b>reconnect &lt;seconds&gt;</b> - disable client reconnecting.
+     * Reconnecting clients will receive a HTTP 503 response with
+     * {@value javax.ws.rs.core.HttpHeaders#RETRY_AFTER} set to the amount of
+     * milliseconds specified.</li>
+     * </ul>
+     *
+     * @param command command to be processed.
+     * @return message about processing result.
+     * @throws BadRequestException in case the command is not recognized or not specified.
+     */
+    @POST
+    @Path("commands")
+    public String processCommand(String command) {
+        if (command == null || command.isEmpty()) {
+            throw new BadRequestException("No command specified.");
+        }
+
+        if ("disconnect".equals(command)) {
+            broadcaster.closeAll();
+            return "Disconnected.";
+        } else if (command.length() > "reconnect ".length() && command.startsWith("reconnect ")) {
+            final String when = command.substring("reconnect ".length());
+            try {
+                reconnectDelay = "now".equals(when) ? 0 : Long.parseLong(when);
+                return "Reconnect strategy updated: " + when;
+            } catch (NumberFormatException ignore) {
+                // ignored
+            }
+        }
+
+        throw new BadRequestException("Command not recognized: '" + command + "'");
+    }
+
+    /**
+     * Connect or re-connect to SSE event stream.
+     *
+     * @param lastEventId Value of custom SSE HTTP <tt>{@value SseFeature#LAST_EVENT_ID_HEADER}</tt> header.
+     *                    Defaults to {@code -1} if not set.
+     * @return new SSE event output stream representing the (re-)established SSE client connection.
+     * @throws InternalServerErrorException in case replaying missed events to the reconnected output stream fails.
+     * @throws ServiceUnavailableException  in case the reconnect delay is set to a positive value.
+     */
+    @GET
+    @Path("events")
+    @Produces(SseFeature.SERVER_SENT_EVENTS)
+    public EventOutput itemEvents(
+            @HeaderParam(SseFeature.LAST_EVENT_ID_HEADER) @DefaultValue("-1") int lastEventId) {
+        final EventOutput eventOutput = new EventOutput();
+
+        if (lastEventId >= 0) {
+            LOGGER.info("Received last event id :" + lastEventId);
+
+            // decide the reconnect handling strategy based on current reconnect delay value.
+            final long delay = reconnectDelay;
+            if (delay > 0) {
+                LOGGER.info("Non-zero reconnect delay [" + delay + "] - responding with HTTP 503.");
+                throw new ServiceUnavailableException(delay);
+            } else {
+                LOGGER.info("Zero reconnect delay - reconnecting.");
+                replayMissedEvents(lastEventId, eventOutput);
+            }
+        }
+
+        if (!broadcaster.add(eventOutput)) {
+            LOGGER.severe("!!! Unable to add new event output to the broadcaster !!!");
+            // let's try to force a 5s delayed client reconnect attempt
+            throw new ServiceUnavailableException(5L);
+        }
+
+        return eventOutput;
+    }
+
+    private void replayMissedEvents(final int lastEventId, final EventOutput eventOutput) {
+        try {
+            storeLock.readLock().lock();
+            final int firstUnreceived = lastEventId + 1;
+            final int missingCount = itemStore.size() - firstUnreceived;
+            if (missingCount > 0) {
+                LOGGER.info("Replaying events - starting with id " + firstUnreceived);
+                final ListIterator<String> it = itemStore.subList(firstUnreceived, itemStore.size()).listIterator();
+                while (it.hasNext()) {
+                    eventOutput.write(createItemEvent(it.nextIndex() + firstUnreceived, it.next()));
+                }
+            } else {
+                LOGGER.info("No events to replay.");
+            }
+        } catch (IOException ex) {
+            throw new InternalServerErrorException("Error replaying missed events", ex);
+        } finally {
+            storeLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Add new item to the item store.
+     *
+     * Invoking this method will fire 2 new SSE events - 1st about newly added item and 2nd about the new item store size.
+     *
+     * @param name item name.
+     */
+    @POST
+    public void addItem(@FormParam("name") String name) {
+        final int eventId;
+        try {
+            storeLock.writeLock().lock();
+            eventId = itemStore.size();
+            itemStore.add(name);
+            // Broadcasting an un-named event with the name of the newly added item in data
+            broadcaster.broadcast(createItemEvent(eventId, name));
+            // Broadcasting a named "size" event with the current size of the items collection in data
+            broadcaster.broadcast(new OutboundEvent.Builder().name("size").data(Integer.class, eventId + 1).build());
+        } finally {
+            storeLock.writeLock().unlock();
+        }
+    }
+
+    private OutboundEvent createItemEvent(final int eventId, final String name) {
+        Logger.getLogger(ItemStoreResource.class.getName()).info("Creating event id [" + eventId + "] name [" + name + "]");
+        return new OutboundEvent.Builder().id("" + eventId).data(String.class, name).build();
+    }
+}
diff --git a/tests/integration/servlet-3-sse-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreResourceITCase.java b/tests/integration/servlet-3-sse-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreResourceITCase.java
new file mode 100644
index 0000000..70bdd76
--- /dev/null
+++ b/tests/integration/servlet-3-sse-1/src/test/java/org/glassfish/jersey/tests/integration/servlet_3_sse_1/ItemStoreResourceITCase.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlet_3_sse_1;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
+import org.glassfish.jersey.media.sse.EventListener;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.media.sse.InboundEvent;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.describedAs;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Item store test.
+ *
+ * @author Marek Potociar (marek.potociar at oracle.com)
+ */
+@Ignore
+public class ItemStoreResourceITCase extends JerseyTest {
+
+    private static final Logger LOGGER = Logger.getLogger(ItemStoreResourceITCase.class.getName());
+    private static final int MAX_LISTENERS = 5;
+    private static final int MAX_ITEMS = 10;
+
+
+    @Override
+    protected Application configure() {
+        return new ItemStoreApp();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.property(ClientProperties.CONNECT_TIMEOUT, 15000)
+                .property(ClientProperties.READ_TIMEOUT, 2000)
+                .property(ClientProperties.ASYNC_THREADPOOL_SIZE, MAX_LISTENERS + 1)
+                .connectorProvider(new GrizzlyConnectorProvider());
+    }
+
+    @Override
+    protected URI getBaseUri() {
+        final UriBuilder baseUriBuilder = UriBuilder.fromUri(super.getBaseUri());
+        final boolean externalFactoryInUse = getTestContainerFactory() instanceof ExternalTestContainerFactory;
+        return externalFactoryInUse ? baseUriBuilder.path("resources").build() : baseUriBuilder.build();
+    }
+
+    /**
+     * Test the item addition, addition event broadcasting and item retrieval from {@link ItemStoreResource}.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testItemsStore() throws Exception {
+        final List<String> items = Collections.unmodifiableList(Arrays.asList(
+                "foo",
+                "bar",
+                "baz"));
+        final WebTarget itemsTarget = target("items");
+        final CountDownLatch latch = new CountDownLatch(items.size() * MAX_LISTENERS * 2); // countdown on all events
+        final List<Queue<Integer>> indexQueues = new ArrayList<Queue<Integer>>(MAX_LISTENERS);
+        final EventSource[] sources = new EventSource[MAX_LISTENERS];
+        final AtomicInteger sizeEventsCount = new AtomicInteger(0);
+
+        for (int i = 0; i < MAX_LISTENERS; i++) {
+            final int id = i;
+            final EventSource es = EventSource.target(itemsTarget.path("events"))
+                    .named("SOURCE " + id).build();
+            sources[id] = es;
+
+            final Queue<Integer> indexes = new ConcurrentLinkedQueue<Integer>();
+            indexQueues.add(indexes);
+
+            es.register(new EventListener() {
+                @Override
+                @SuppressWarnings("MagicNumber")
+                public void onEvent(InboundEvent inboundEvent) {
+                    try {
+                        if (inboundEvent.getName() == null) {
+                            final String data = inboundEvent.readData();
+                            LOGGER.info("[-i-] SOURCE " + id + ": Received event id=" + inboundEvent.getId() + " data=" + data);
+                            indexes.add(items.indexOf(data));
+                        } else if ("size".equals(inboundEvent.getName())) {
+                            sizeEventsCount.incrementAndGet();
+                        }
+                    } catch (ProcessingException ex) {
+                        LOGGER.log(Level.SEVERE, "[-x-] SOURCE " + id + ": Error getting event data.", ex);
+                        indexes.add(-999);
+                    } finally {
+                        latch.countDown();
+                    }
+                }
+            });
+        }
+
+        try {
+            open(sources);
+
+            for (String item : items) {
+                postItem(itemsTarget, item);
+            }
+
+            assertTrue("Waiting to receive all events has timed out.",
+                    latch.await((1000 + MAX_LISTENERS * EventSource.RECONNECT_DEFAULT) * getAsyncTimeoutMultiplier(),
+                            TimeUnit.MILLISECONDS));
+
+            // need to force disconnect on server in order for EventSource.close(...) to succeed with HttpUrlConnection
+            sendCommand(itemsTarget, "disconnect");
+        } finally {
+            close(sources);
+        }
+
+        String postedItems = itemsTarget.request().get(String.class);
+        for (String item : items) {
+            assertTrue("Item '" + item + "' not stored on server.", postedItems.contains(item));
+        }
+
+        int queueId = 0;
+        for (Queue<Integer> indexes : indexQueues) {
+            for (int i = 0; i < items.size(); i++) {
+                assertTrue("Event for '" + items.get(i) + "' not received in queue " + queueId, indexes.contains(i));
+            }
+            assertEquals("Not received the expected number of events in queue " + queueId, items.size(), indexes.size());
+            queueId++;
+        }
+
+        assertEquals("Number of received 'size' events does not match.", items.size() * MAX_LISTENERS, sizeEventsCount.get());
+    }
+
+    /**
+     * Test the {@link EventSource} reconnect feature.
+     *
+     * @throws Exception in case of a test failure.
+     */
+    @Test
+    public void testEventSourceReconnect() throws Exception {
+        final WebTarget itemsTarget = target("items");
+        final CountDownLatch latch = new CountDownLatch(MAX_ITEMS * MAX_LISTENERS * 2); // countdown only on new item events
+        final List<Queue<String>> receivedQueues = new ArrayList<Queue<String>>(MAX_LISTENERS);
+        final EventSource[] sources = new EventSource[MAX_LISTENERS];
+
+        for (int i = 0; i < MAX_LISTENERS; i++) {
+            final int id = i;
+            final EventSource es = EventSource.target(itemsTarget.path("events")).named("SOURCE " + id).build();
+            sources[id] = es;
+
+            final Queue<String> received = new ConcurrentLinkedQueue<String>();
+            receivedQueues.add(received);
+
+            es.register(new EventListener() {
+                @Override
+                public void onEvent(InboundEvent inboundEvent) {
+                    try {
+                        if (inboundEvent.getName() == null) {
+                            latch.countDown();
+                            final String data = inboundEvent.readData();
+                            LOGGER.info("[-i-] SOURCE " + id + ": Received event id=" + inboundEvent.getId() + " data=" + data);
+                            received.add(data);
+                        }
+                    } catch (ProcessingException ex) {
+                        LOGGER.log(Level.SEVERE, "[-x-] SOURCE " + id + ": Error getting event data.", ex);
+                        received.add("[data processing error]");
+                    }
+                }
+            });
+        }
+
+        final String[] postedItems = new String[MAX_ITEMS * 2];
+        try {
+            open(sources);
+
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                final String item = String.format("round-1-%02d", i);
+                postItem(itemsTarget, item);
+                postedItems[i] = item;
+                sendCommand(itemsTarget, "disconnect");
+                Thread.sleep(100);
+            }
+
+            final int reconnectDelay = 1;
+            sendCommand(itemsTarget, "reconnect " + reconnectDelay);
+            sendCommand(itemsTarget, "disconnect");
+
+            Thread.sleep(reconnectDelay * 1000);
+
+            for (int i = 0; i < MAX_ITEMS; i++) {
+                final String item = String.format("round-2-%02d", i);
+                postedItems[i + MAX_ITEMS] = item;
+                postItem(itemsTarget, item);
+            }
+
+            sendCommand(itemsTarget, "reconnect now");
+
+            assertTrue("Waiting to receive all events has timed out.",
+                    latch.await((1 + MAX_LISTENERS * (MAX_ITEMS + 1) * reconnectDelay) * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
+
+            // need to force disconnect on server in order for EventSource.close(...) to succeed with HttpUrlConnection
+            sendCommand(itemsTarget, "disconnect");
+        } finally {
+            close(sources);
+        }
+
+        final String storedItems = itemsTarget.request().get(String.class);
+        for (String item : postedItems) {
+            assertThat("Posted item '" + item + "' stored on server", storedItems, containsString(item));
+        }
+
+        int sourceId = 0;
+        for (Queue<String> queue : receivedQueues) {
+            assertThat("Received events in source " + sourceId, queue,
+                    describedAs("Collection containing %0", hasItems(postedItems), Arrays.asList(postedItems).toString()));
+            assertThat("Size of received queue for source " + sourceId, queue.size(), equalTo(postedItems.length));
+            sourceId++;
+        }
+    }
+
+    private static void postItem(final WebTarget itemsTarget, final String item) {
+        final Response response = itemsTarget.request().post(Entity.form(new Form("name", item)));
+        assertEquals("Posting new item has failed.", 204, response.getStatus());
+        LOGGER.info("[-i-] POSTed item: '" + item + "'");
+    }
+
+    private static void open(final EventSource[] sources) {
+        int i = 0;
+        for (EventSource source : sources) {
+            source.open();
+            LOGGER.info("[-->] SOURCE " + i++ + " opened.");
+        }
+    }
+
+    private static void close(final EventSource[] sources) {
+        int i = 0;
+        for (EventSource source : sources) {
+            if (source.isOpen()) {
+                assertTrue("Waiting to close a source has timed out.", source.close(1, TimeUnit.SECONDS));
+//                    source.close(100, TimeUnit.MILLISECONDS);
+                LOGGER.info("[<--] SOURCE " + i++ + " closed.");
+            }
+        }
+    }
+
+    private static void sendCommand(final WebTarget itemsTarget, final String command) {
+        final Response response = itemsTarget.path("commands").request().post(Entity.text(command));
+        assertEquals("'" + command + "' command has failed.", 200, response.getStatus());
+        LOGGER.info("[-!-] COMMAND '" + command + "' has been processed.");
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/pom.xml b/tests/integration/servlet-request-wrapper-binding-2/pom.xml
new file mode 100644
index 0000000..b47561b
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-request-wrappper-binding-2</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-request-wrapper-binding-2</name>
+
+    <description>Servlet integration test - Request wrapper binding 2 - ServletContainerProviderFactory, ServletContainerProvider</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-servlet-portability</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/JaxRsApplicationAutodetected.java b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/JaxRsApplicationAutodetected.java
new file mode 100644
index 0000000..fe3f465
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/JaxRsApplicationAutodetected.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding2;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Test application. This one gets registered automatically.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("autodetected")
+public class JaxRsApplicationAutodetected extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {
+            {
+                add(RequestResponseInjectedResource.class);
+                add(RequestResponseInjectedSingletonResource.class);
+            }
+        };
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseInjectedResource.java b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseInjectedResource.java
new file mode 100644
index 0000000..47370b8
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseInjectedResource.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding2;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+/**
+ * Test resource that gets injected with the actual {@link HttpServletRequest} and
+ * {@link HttpServletResponse} instance, so that we could testify custom implementations
+ * has been used there.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+public class RequestResponseInjectedResource {
+
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+
+    @Context
+    HttpServletRequest request;
+
+    @Context
+    HttpServletResponse response;
+
+    @GET
+    @Path("requestType")
+    public String getRequestType() throws Exception {
+
+        // make sure we can access underlying request from another thread
+        return executor.submit(new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                return ((HttpServletRequestWrapper) request).getRequest().getClass().getName();
+            }
+        }).get();
+    }
+
+    @GET
+    @Path("responseType")
+    public String getResponseType() {
+
+        return ((HttpServletResponseWrapper) response).getResponse().getClass().getName();
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseInjectedSingletonResource.java b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseInjectedSingletonResource.java
new file mode 100644
index 0000000..f916f7e
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseInjectedSingletonResource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding2;
+
+import javax.inject.Singleton;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+/**
+ * Test resource that gets injected with the actual {@link HttpServletRequest} and
+ * {@link HttpServletResponse} instance, so that we could testify custom implementations
+ * has been used there.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/singleton")
+@Singleton
+public class RequestResponseInjectedSingletonResource {
+
+    @Context
+    HttpServletRequest request;
+
+    @Context
+    HttpServletResponse response;
+
+    @GET
+    @Path("requestType")
+    public String getRequestType() {
+
+        return ((HttpServletRequestWrapper) request).getRequest().getClass().getName();
+    }
+
+    @GET
+    @Path("request/param")
+    public String getRequestAttr() {
+
+        return request.getParameter("q");
+    }
+
+    @GET
+    @Path("responseType")
+    public String getResponseType() {
+
+        return ((HttpServletResponseWrapper) response).getResponse().getClass().getName();
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java
new file mode 100644
index 0000000..db50a72
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding2/RequestResponseWrapperProvider.java
@@ -0,0 +1,839 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding2;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import javax.ws.rs.core.GenericType;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
+
+import org.glassfish.jersey.inject.hk2.DelayedHk2InjectionManager;
+import org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.ReferencingFactory;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.ComponentProvider;
+import org.glassfish.jersey.server.spi.RequestScopedInitializer;
+import org.glassfish.jersey.servlet.internal.spi.NoOpServletContainerProvider;
+import org.glassfish.jersey.servlet.internal.spi.RequestContextProvider;
+import org.glassfish.jersey.servlet.internal.spi.RequestScopedInitializerProvider;
+
+import org.glassfish.hk2.api.DescriptorType;
+import org.glassfish.hk2.api.DescriptorVisibility;
+import org.glassfish.hk2.api.PerLookup;
+import org.glassfish.hk2.api.ServiceHandle;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.hk2.api.TypeLiteral;
+import org.glassfish.hk2.utilities.AbstractActiveDescriptor;
+import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
+
+import org.jvnet.hk2.internal.ServiceHandleImpl;
+
+/**
+ * Servlet container provider that wraps the original Servlet request/response.
+ * The request wrapper contains a direct reference to the underlying container request
+ * in case it gets injected into a request scoped component.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class RequestResponseWrapperProvider extends NoOpServletContainerProvider {
+
+    private final Type REQUEST_TYPE = (new TypeLiteral<Ref<HttpServletRequestWrapper>>() {
+    }).getType();
+    private final Type RESPONSE_TYPE = (new TypeLiteral<Ref<HttpServletResponseWrapper>>() {
+    }).getType();
+
+    public static class DescriptorProvider implements ComponentProvider {
+
+        @Override
+        public void initialize(InjectionManager injectionManager) {
+            ServiceLocator locator = getServiceLocator(injectionManager);
+            ServiceLocatorUtilities.addOneDescriptor(locator, new HttpServletRequestDescriptor(locator));
+        }
+
+        @Override
+        public boolean bind(Class<?> component, Set<Class<?>> providerContracts) {
+            return false;
+        }
+
+        @Override
+        public void done() {
+            // nop
+        }
+    }
+
+    /**
+     * Subclass standard wrapper so that we make 100 % sure we are getting the right type.
+     * It is also final, i.e. not proxiable, which we workaround by using custom http servlet request impl.
+     */
+    public static final class RequestWrapper extends HttpServletRequestWrapper {
+
+        public RequestWrapper(HttpServletRequest request) {
+            super(request);
+        }
+    }
+
+    /**
+     * Subclass standard wrapper so that we make 100 % sure we are getting the right type.
+     * It is also final, i.e. not proxiable, which we workaround by using custom http servlet response impl.
+     */
+    public static final class ResponseWrapper extends HttpServletResponseWrapper {
+
+        public ResponseWrapper(HttpServletResponse response) {
+            super(response);
+        }
+    }
+
+    @Override
+    public boolean bindsServletRequestResponse() {
+        return true;
+    }
+
+    @Override
+    public RequestScopedInitializerProvider getRequestScopedInitializerProvider() {
+        return new RequestScopedInitializerProvider() {
+
+            @Override
+            public RequestScopedInitializer get(final RequestContextProvider context) {
+                return new RequestScopedInitializer() {
+                    @Override
+                    public void initialize(InjectionManager injectionManager) {
+                        ServiceLocator locator = getServiceLocator(injectionManager);
+                        locator.<Ref<HttpServletRequest>>getService(REQUEST_TYPE)
+                                .set(finalWrap(context.getHttpServletRequest()));
+                        locator.<Ref<HttpServletResponse>>getService(RESPONSE_TYPE)
+                                .set(finalWrap(context.getHttpServletResponse()));
+                    }
+                };
+            }
+        };
+    }
+
+    private final class Binder extends AbstractBinder {
+
+        @Override
+        protected void configure() {
+
+            bindFactory(HttpServletRequestReferencingFactory.class)
+                    .to(HttpServletRequestWrapper.class).in(RequestScoped.class);
+
+            bindFactory(ReferencingFactory.<HttpServletRequestWrapper>referenceFactory())
+                    .to(new GenericType<Ref<HttpServletRequestWrapper>>() {
+                    }).in(RequestScoped.class);
+
+            bindFactory(HttpServletResponseFactory.class).to(HttpServletResponse.class);
+
+            bindFactory(HttpServletResponseReferencingFactory.class)
+                    .to(HttpServletResponseWrapper.class).in(RequestScoped.class);
+
+            bindFactory(ReferencingFactory.<HttpServletResponseWrapper>referenceFactory())
+                    .to(new GenericType<Ref<HttpServletResponseWrapper>>() {
+                    }).in(RequestScoped.class);
+
+        }
+    }
+
+    private static class HttpServletRequestDescriptor extends AbstractActiveDescriptor<HttpServletRequest> {
+
+        static Set<Type> advertisedContracts = new HashSet<Type>() {
+            {
+                add(HttpServletRequest.class);
+            }
+        };
+
+        final ServiceLocator locator;
+        volatile javax.inject.Provider<Ref<HttpServletRequestWrapper>> request;
+
+        public HttpServletRequestDescriptor(final ServiceLocator locator) {
+            super(advertisedContracts,
+                    PerLookup.class,
+                    null, new HashSet<Annotation>(),
+                    DescriptorType.CLASS, DescriptorVisibility.LOCAL,
+                    0, null, null, null, null);
+            this.locator = locator;
+        }
+
+        @Override
+        public Class<?> getImplementationClass() {
+            return HttpServletRequest.class;
+        }
+
+        @Override
+        public Type getImplementationType() {
+            return getImplementationClass();
+        }
+
+        @Override
+        public synchronized String getImplementation() {
+            return HttpServletRequest.class.getName();
+        }
+
+        @Override
+        public HttpServletRequest create(ServiceHandle<?> serviceHandle) {
+            if (request == null) {
+                request = locator.getService(new TypeLiteral<Provider<Ref<HttpServletRequestWrapper>>>() {
+                }.getType());
+            }
+
+            boolean direct = false;
+
+            if (serviceHandle instanceof ServiceHandleImpl) {
+                final ServiceHandleImpl serviceHandleImpl = (ServiceHandleImpl) serviceHandle;
+                final Class<? extends Annotation> scopeAnnotation =
+                        serviceHandleImpl.getOriginalRequest().getInjecteeDescriptor().getScopeAnnotation();
+
+                if (scopeAnnotation == RequestScoped.class || scopeAnnotation == null) {
+                    direct = true;
+                }
+            }
+
+
+            return !direct ? new HttpServletRequestWrapper(new MyHttpServletRequestImpl() {
+                @Override
+                HttpServletRequest getHttpServletRequest() {
+                    return request.get().get();
+                }
+            }) {
+                @Override
+                public ServletRequest getRequest() {
+                    return request.get().get();
+                }
+
+            }
+                    : new HttpServletRequestWrapper(request.get().get());
+        }
+    }
+
+    private static class HttpServletResponseFactory implements Supplier<HttpServletResponse> {
+        private final javax.inject.Provider<Ref<HttpServletResponseWrapper>> response;
+
+        @Inject
+        public HttpServletResponseFactory(javax.inject.Provider<Ref<HttpServletResponseWrapper>> response) {
+            this.response = response;
+        }
+
+        @Override
+        @PerLookup
+        public HttpServletResponse get() {
+            return new HttpServletResponseWrapper(new HttpServletResponse() {
+
+                private HttpServletResponse getHttpServletResponse() {
+                    return response.get().get();
+                }
+
+                @Override
+                public void addCookie(Cookie cookie) {
+                    getHttpServletResponse().addCookie(cookie);
+                }
+
+                @Override
+                public boolean containsHeader(String s) {
+                    return getHttpServletResponse().containsHeader(s);
+                }
+
+                @Override
+                public String encodeURL(String s) {
+                    return getHttpServletResponse().encodeURL(s);
+                }
+
+                @Override
+                public String encodeRedirectURL(String s) {
+                    return getHttpServletResponse().encodeRedirectURL(s);
+                }
+
+                @Override
+                public String encodeUrl(String s) {
+                    return getHttpServletResponse().encodeUrl(s);
+                }
+
+                @Override
+                public String encodeRedirectUrl(String s) {
+                    return getHttpServletResponse().encodeRedirectUrl(s);
+                }
+
+                @Override
+                public void sendError(int i, String s) throws IOException {
+                    getHttpServletResponse().sendError(i, s);
+                }
+
+                @Override
+                public void sendError(int i) throws IOException {
+                    getHttpServletResponse().sendError(i);
+                }
+
+                @Override
+                public void sendRedirect(String s) throws IOException {
+                    getHttpServletResponse().sendRedirect(s);
+                }
+
+                @Override
+                public void setDateHeader(String s, long l) {
+                    getHttpServletResponse().setDateHeader(s, l);
+                }
+
+                @Override
+                public void addDateHeader(String s, long l) {
+                    getHttpServletResponse().addDateHeader(s, l);
+                }
+
+                @Override
+                public void setHeader(String h, String v) {
+                    getHttpServletResponse().setHeader(h, v);
+                }
+
+                public Collection<String> getHeaderNames() {
+                    return getHttpServletResponse().getHeaderNames();
+                }
+
+                public Collection<String> getHeaders(String s) {
+                    return getHttpServletResponse().getHeaders(s);
+                }
+
+                public String getHeader(String s) {
+                    return getHttpServletResponse().getHeader(s);
+                }
+
+                @Override
+                public void addHeader(String h, String v) {
+                    getHttpServletResponse().addHeader(h, v);
+                }
+
+                @Override
+                public void setIntHeader(String s, int i) {
+                    getHttpServletResponse().setIntHeader(s, i);
+                }
+
+                @Override
+                public void addIntHeader(String s, int i) {
+                    getHttpServletResponse().addIntHeader(s, i);
+                }
+
+                @Override
+                public void setStatus(int i) {
+                    getHttpServletResponse().setStatus(i);
+                }
+
+                @Override
+                public int getStatus() {
+                    return getHttpServletResponse().getStatus();
+                }
+
+                @Override
+                public void setStatus(int i, String s) {
+                    getHttpServletResponse().setStatus(i, s);
+                }
+
+                @Override
+                public String getCharacterEncoding() {
+                    return getHttpServletResponse().getCharacterEncoding();
+                }
+
+                @Override
+                public String getContentType() {
+                    return getHttpServletResponse().getContentType();
+                }
+
+                @Override
+                public ServletOutputStream getOutputStream() throws IOException {
+                    return getHttpServletResponse().getOutputStream();
+                }
+
+                @Override
+                public PrintWriter getWriter() throws IOException {
+                    return getHttpServletResponse().getWriter();
+                }
+
+                @Override
+                public void setCharacterEncoding(String s) {
+                    getHttpServletResponse().setCharacterEncoding(s);
+                }
+
+                @Override
+                public void setContentLength(int i) {
+                    getHttpServletResponse().setContentLength(i);
+                }
+
+                @Override
+                public void setContentType(String s) {
+                    getHttpServletResponse().setContentType(s);
+                }
+
+                @Override
+                public void setBufferSize(int i) {
+                    getHttpServletResponse().setBufferSize(i);
+                }
+
+                @Override
+                public int getBufferSize() {
+                    return getHttpServletResponse().getBufferSize();
+                }
+
+                @Override
+                public void flushBuffer() throws IOException {
+                    getHttpServletResponse().flushBuffer();
+                }
+
+                @Override
+                public void resetBuffer() {
+                    getHttpServletResponse().resetBuffer();
+                }
+
+                @Override
+                public boolean isCommitted() {
+                    return getHttpServletResponse().isCommitted();
+                }
+
+                @Override
+                public void reset() {
+                    getHttpServletResponse().reset();
+                }
+
+                @Override
+                public void setLocale(Locale locale) {
+                    getHttpServletResponse().setLocale(locale);
+                }
+
+                @Override
+                public Locale getLocale() {
+                    return getHttpServletResponse().getLocale();
+                }
+            }
+            ) {
+                @Override
+                public ServletResponse getResponse() {
+                    return response.get().get();
+                }
+            };
+        }
+
+    }
+
+    @SuppressWarnings("JavaDoc")
+    private static class HttpServletRequestReferencingFactory extends ReferencingFactory<HttpServletRequestWrapper> {
+
+        @Inject
+        public HttpServletRequestReferencingFactory(
+                final javax.inject.Provider<Ref<HttpServletRequestWrapper>> referenceFactory) {
+
+            super(referenceFactory);
+        }
+    }
+
+    @SuppressWarnings("JavaDoc")
+    private static class HttpServletResponseReferencingFactory extends ReferencingFactory<HttpServletResponseWrapper> {
+
+        @Inject
+        public HttpServletResponseReferencingFactory(
+                final javax.inject.Provider<Ref<HttpServletResponseWrapper>> referenceFactory) {
+
+            super(referenceFactory);
+        }
+
+    }
+
+    @Override
+    public void configure(final ResourceConfig resourceConfig) throws ServletException {
+        resourceConfig.register(new Binder());
+    }
+
+    private HttpServletRequest finalWrap(final HttpServletRequest request) {
+        return new RequestWrapper(request);
+    }
+
+    private HttpServletResponse finalWrap(final HttpServletResponse response) {
+        return new ResponseWrapper(response);
+    }
+
+    private abstract static class MyHttpServletRequestImpl implements HttpServletRequest {
+
+        @Override
+        public String getAuthType() {
+            return getHttpServletRequest().getAuthType();
+        }
+
+        @Override
+        public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
+            return getHttpServletRequest().authenticate(response);
+        }
+
+        @Override
+        public boolean isAsyncSupported() {
+            return getHttpServletRequest().isAsyncSupported();
+        }
+
+        @Override
+        public boolean isAsyncStarted() {
+            return getHttpServletRequest().isAsyncStarted();
+        }
+
+        @Override
+        public AsyncContext startAsync() throws IllegalStateException {
+            return getHttpServletRequest().startAsync();
+        }
+
+        @Override
+        public AsyncContext startAsync(ServletRequest request, ServletResponse response) throws IllegalStateException {
+            return getHttpServletRequest().startAsync(request, response);
+        }
+
+        abstract HttpServletRequest getHttpServletRequest();
+
+        @Override
+        public Cookie[] getCookies() {
+            return getHttpServletRequest().getCookies();
+        }
+
+        @Override
+        public long getDateHeader(String s) {
+            return getHttpServletRequest().getDateHeader(s);
+        }
+
+        @Override
+        public Part getPart(String s) throws ServletException, IOException {
+            return getHttpServletRequest().getPart(s);
+        }
+
+        @Override
+        public Collection<Part> getParts() throws ServletException, IOException {
+            return getHttpServletRequest().getParts();
+        }
+
+        @Override
+        public String getHeader(String s) {
+            return getHttpServletRequest().getHeader(s);
+        }
+
+        @Override
+        public Enumeration getHeaders(String s) {
+            return getHttpServletRequest().getHeaders(s);
+        }
+
+        @Override
+        public Enumeration getHeaderNames() {
+            return getHttpServletRequest().getHeaderNames();
+        }
+
+        @Override
+        public int getIntHeader(String s) {
+            return getHttpServletRequest().getIntHeader(s);
+        }
+
+        @Override
+        public String getMethod() {
+            return getHttpServletRequest().getMethod();
+        }
+
+        @Override
+        public String getPathInfo() {
+            return getHttpServletRequest().getPathInfo();
+        }
+
+        @Override
+        public String getPathTranslated() {
+            return getHttpServletRequest().getPathTranslated();
+        }
+
+        @Override
+        public String getContextPath() {
+            return getHttpServletRequest().getContextPath();
+        }
+
+        @Override
+        public String getQueryString() {
+            return getHttpServletRequest().getQueryString();
+        }
+
+        @Override
+        public String getRemoteUser() {
+            return getHttpServletRequest().getRemoteUser();
+        }
+
+        @Override
+        public boolean isUserInRole(String s) {
+            return getHttpServletRequest().isUserInRole(s);
+        }
+
+        @Override
+        public Principal getUserPrincipal() {
+            return getHttpServletRequest().getUserPrincipal();
+        }
+
+        @Override
+        public String getRequestedSessionId() {
+            return getHttpServletRequest().getRequestedSessionId();
+        }
+
+        @Override
+        public String getRequestURI() {
+            return getHttpServletRequest().getRequestURI();
+        }
+
+        @Override
+        public StringBuffer getRequestURL() {
+            return getHttpServletRequest().getRequestURL();
+        }
+
+        @Override
+        public String getServletPath() {
+            return getHttpServletRequest().getServletPath();
+        }
+
+        @Override
+        public HttpSession getSession(boolean b) {
+            return getHttpServletRequest().getSession(b);
+        }
+
+        @Override
+        public HttpSession getSession() {
+            return getHttpServletRequest().getSession();
+        }
+
+        @Override
+        public boolean isRequestedSessionIdValid() {
+            return getHttpServletRequest().isRequestedSessionIdValid();
+        }
+
+        @Override
+        public boolean isRequestedSessionIdFromCookie() {
+            return getHttpServletRequest().isRequestedSessionIdFromCookie();
+        }
+
+        @Override
+        public boolean isRequestedSessionIdFromURL() {
+            return getHttpServletRequest().isRequestedSessionIdFromURL();
+        }
+
+        @Override
+        public boolean isRequestedSessionIdFromUrl() {
+            return getHttpServletRequest().isRequestedSessionIdFromUrl();
+        }
+
+        @Override
+        public Object getAttribute(String s) {
+            return getHttpServletRequest().getAttribute(s);
+        }
+
+        @Override
+        public Enumeration getAttributeNames() {
+            return getHttpServletRequest().getAttributeNames();
+        }
+
+        @Override
+        public String getCharacterEncoding() {
+            return getHttpServletRequest().getCharacterEncoding();
+        }
+
+        @Override
+        public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
+            getHttpServletRequest().setCharacterEncoding(s);
+        }
+
+        @Override
+        public int getContentLength() {
+            return getHttpServletRequest().getContentLength();
+        }
+
+        @Override
+        public String getContentType() {
+            return getHttpServletRequest().getContentType();
+        }
+
+        @Override
+        public ServletInputStream getInputStream() throws IOException {
+            return getHttpServletRequest().getInputStream();
+        }
+
+        @Override
+        public String getParameter(String s) {
+            return getHttpServletRequest().getParameter(s);
+        }
+
+        @Override
+        public Enumeration getParameterNames() {
+            return getHttpServletRequest().getParameterNames();
+        }
+
+        @Override
+        public String[] getParameterValues(String s) {
+            return getHttpServletRequest().getParameterValues(s);
+        }
+
+        @Override
+        public Map getParameterMap() {
+            return getHttpServletRequest().getParameterMap();
+        }
+
+        @Override
+        public String getProtocol() {
+            return getHttpServletRequest().getProtocol();
+        }
+
+        @Override
+        public String getScheme() {
+            return getHttpServletRequest().getScheme();
+        }
+
+        @Override
+        public String getServerName() {
+            return getHttpServletRequest().getServerName();
+        }
+
+        @Override
+        public int getServerPort() {
+            return getHttpServletRequest().getServerPort();
+        }
+
+        @Override
+        public BufferedReader getReader() throws IOException {
+            return getHttpServletRequest().getReader();
+        }
+
+        @Override
+        public String getRemoteAddr() {
+            return getHttpServletRequest().getRemoteAddr();
+        }
+
+        @Override
+        public String getRemoteHost() {
+            return getHttpServletRequest().getRemoteHost();
+        }
+
+        @Override
+        public void setAttribute(String s, Object o) {
+            getHttpServletRequest().setAttribute(s, o);
+        }
+
+        @Override
+        public void removeAttribute(String s) {
+            getHttpServletRequest().removeAttribute(s);
+        }
+
+        @Override
+        public Locale getLocale() {
+            return getHttpServletRequest().getLocale();
+        }
+
+        @Override
+        public Enumeration getLocales() {
+            return getHttpServletRequest().getLocales();
+        }
+
+        @Override
+        public boolean isSecure() {
+            return getHttpServletRequest().isSecure();
+        }
+
+        @Override
+        public RequestDispatcher getRequestDispatcher(String s) {
+            return getHttpServletRequest().getRequestDispatcher(s);
+        }
+
+        @Override
+        public String getRealPath(String s) {
+            return getHttpServletRequest().getRealPath(s);
+        }
+
+        @Override
+        public int getRemotePort() {
+            return getHttpServletRequest().getRemotePort();
+        }
+
+        @Override
+        public String getLocalName() {
+            return getHttpServletRequest().getLocalName();
+        }
+
+        @Override
+        public String getLocalAddr() {
+            return getHttpServletRequest().getLocalAddr();
+        }
+
+        @Override
+        public int getLocalPort() {
+            return getHttpServletRequest().getLocalPort();
+        }
+
+        @Override
+        public DispatcherType getDispatcherType() {
+            return getHttpServletRequest().getDispatcherType();
+        }
+
+        @Override
+        public AsyncContext getAsyncContext() {
+            return getHttpServletRequest().getAsyncContext();
+        }
+
+        @Override
+        public ServletContext getServletContext() {
+            return getHttpServletRequest().getServletContext();
+        }
+
+        @Override
+        public void logout() throws ServletException {
+            getHttpServletRequest().logout();
+        }
+
+        @Override
+        public void login(String u, String p) throws ServletException {
+            getHttpServletRequest().login(u, p);
+        }
+    }
+
+    private static ServiceLocator getServiceLocator(InjectionManager injectionManager) {
+        if (injectionManager instanceof ImmediateHk2InjectionManager) {
+            return  ((ImmediateHk2InjectionManager) injectionManager).getServiceLocator();
+        } else if (injectionManager instanceof DelayedHk2InjectionManager) {
+            return  ((DelayedHk2InjectionManager) injectionManager).getServiceLocator();
+        } else {
+            throw new RuntimeException("Invalid InjectionManager");
+        }
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider b/tests/integration/servlet-request-wrapper-binding-2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
new file mode 100644
index 0000000..e6780ff
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.RequestResponseWrapperProvider$DescriptorProvider
\ No newline at end of file
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider b/tests/integration/servlet-request-wrapper-binding-2/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
new file mode 100644
index 0000000..543851e
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.RequestResponseWrapperProvider
\ No newline at end of file
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-request-wrapper-binding-2/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..aab05c4
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <servlet>
+        <servlet-name>jerseyApp</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.RequestResponseInjectedResource,
+                org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.RequestResponseInjectedSingletonResource
+            </param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>jerseyApp</servlet-name>
+        <url-pattern>/webxmlconfigured/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/AbstractRequestResponseTypeTest.java b/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/AbstractRequestResponseTypeTest.java
new file mode 100644
index 0000000..5631450
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/AbstractRequestResponseTypeTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper2;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.RequestResponseWrapperProvider;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Make sure that injected request/response instances
+ * are of the types injected by {@link RequestResponseWrapperProvider}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public abstract class AbstractRequestResponseTypeTest extends JerseyTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testRequestType() throws Exception {
+        final String requestType = target(getAppBasePath()).path("requestType").request().get(String.class);
+        assertThat(requestType, is(equalTo(RequestResponseWrapperProvider.RequestWrapper.class.getName())));
+    }
+
+    @Test
+    public void testResponseType() throws Exception {
+        final String requestType = target(getAppBasePath()).path("responseType").request().get(String.class);
+        assertThat(requestType, is(equalTo(RequestResponseWrapperProvider.ResponseWrapper.class.getName())));
+    }
+
+    @Test
+    public void testSingletonRequestType() throws Exception {
+        final String requestType = target(getAppBasePath()).path("singleton/requestType").request().get(String.class);
+        assertThat(requestType, is(equalTo(RequestResponseWrapperProvider.RequestWrapper.class.getName())));
+    }
+
+    @Test
+    public void testSingletonRequestAttr() throws Exception {
+        for (String q : new String[] {"1", "2", "3", "95", "98", "NT", "2000", "XP", "Vista", "7", "8", "10"}) {
+            _testSingletonRequestAttr("one");
+        }
+    }
+
+    public void _testSingletonRequestAttr(String q) throws Exception {
+        final String requestType = target(getAppBasePath()).path("singleton/request/param")
+                .queryParam("q", q).request().get(String.class);
+        assertThat(requestType, is(equalTo(q)));
+    }
+
+    @Test
+    public void testSingletonResponseType() throws Exception {
+        final String requestType = target(getAppBasePath()).path("singleton/responseType").request().get(String.class);
+        assertThat(requestType, is(equalTo(RequestResponseWrapperProvider.ResponseWrapper.class.getName())));
+    }
+
+    protected abstract String getAppBasePath();
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/AutodetectedAppRequestResponseTypeITCase.java b/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/AutodetectedAppRequestResponseTypeITCase.java
new file mode 100644
index 0000000..13a6063
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/AutodetectedAppRequestResponseTypeITCase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper2;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.JaxRsApplicationAutodetected;
+
+/**
+ * Test for the autodetected JAX-RS app.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class AutodetectedAppRequestResponseTypeITCase extends AbstractRequestResponseTypeTest {
+
+    @Override
+    protected String getAppBasePath() {
+        return "autodetected";
+    }
+
+    @Override
+    protected Application configure() {
+        return ResourceConfig.forApplicationClass(JaxRsApplicationAutodetected.class);
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/WebXmlConfiguredAppRequestResponseTypeITCase.java b/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/WebXmlConfiguredAppRequestResponseTypeITCase.java
new file mode 100644
index 0000000..9a028dc
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding-2/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper2/WebXmlConfiguredAppRequestResponseTypeITCase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper2;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding2.RequestResponseInjectedResource;
+
+/**
+ * Test for the web.xml configured JAX-RS app.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class WebXmlConfiguredAppRequestResponseTypeITCase extends AbstractRequestResponseTypeTest {
+
+    @Override
+    protected String getAppBasePath() {
+        return "webxmlconfigured";
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(RequestResponseInjectedResource.class);
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding/pom.xml b/tests/integration/servlet-request-wrapper-binding/pom.xml
new file mode 100644
index 0000000..8e869c4
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-request-wrappper-binding</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-request-wrapper-binding</name>
+
+    <description>Servlet integration test - Request wrapper binding - ServletContainerProviderFactory, ServletContainerProvider</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-servlet-portability</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>${servlet3.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/JaxRsApplicationAutodetected.java b/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/JaxRsApplicationAutodetected.java
new file mode 100644
index 0000000..1fe4126
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/JaxRsApplicationAutodetected.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Test application. This one gets registered automatically.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@ApplicationPath("autodetected")
+public class JaxRsApplicationAutodetected extends Application {
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return new HashSet<Class<?>>() {
+            {
+                add(RequestResponseInjectedResource.class);
+            }
+        };
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/RequestResponseInjectedResource.java b/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/RequestResponseInjectedResource.java
new file mode 100644
index 0000000..54cc690
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/RequestResponseInjectedResource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+
+/**
+ * Test resource that gets injected with the actual {@link HttpServletRequest} and
+ * {@link HttpServletResponse} instance, so that we could testify custom implementations
+ * has been used there.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+public class RequestResponseInjectedResource {
+
+    @Context
+    HttpServletRequest request;
+
+    @Context
+    HttpServletResponse response;
+
+    @GET
+    @Path("requestType")
+    public String getRequestType() {
+
+        return request.getClass().getName();
+    }
+
+    @GET
+    @Path("responseType")
+    public String getResponseType() {
+
+        return response.getClass().getName();
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/RequestResponseWrapperProvider.java b/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/RequestResponseWrapperProvider.java
new file mode 100644
index 0000000..09f40a7
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/main/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper_binding/RequestResponseWrapperProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper_binding;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.server.spi.RequestScopedInitializer;
+import org.glassfish.jersey.servlet.internal.spi.NoOpServletContainerProvider;
+import org.glassfish.jersey.servlet.internal.spi.RequestScopedInitializerProvider;
+
+/**
+ * Servlet container provider that wraps the original Servlet request/response.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class RequestResponseWrapperProvider extends NoOpServletContainerProvider {
+
+    /**
+     * Subclass standard wrapper so that we make 100 % sure we are getting the right type.
+     */
+    public static class RequestWrapper extends HttpServletRequestWrapper {
+
+        public RequestWrapper(HttpServletRequest request) {
+            super(request);
+        }
+    }
+
+    /**
+     * Subclass standard wrapper so that we make 100 % sure we are getting the right type.
+     */
+    public static class ResponseWrapper extends HttpServletResponseWrapper {
+
+        public ResponseWrapper(HttpServletResponse response) {
+            super(response);
+        }
+    }
+
+    @Override
+    public RequestScopedInitializerProvider getRequestScopedInitializerProvider() {
+        return context -> (RequestScopedInitializer) injectionManager -> {
+            injectionManager.<Ref<HttpServletRequest>>getInstance(HTTP_SERVLET_REQUEST_TYPE)
+                    .set(wrapped(context.getHttpServletRequest()));
+            injectionManager.<Ref<HttpServletResponse>>getInstance(HTTP_SERVLET_RESPONSE_TYPE)
+                    .set(wrapped(context.getHttpServletResponse()));
+        };
+    }
+
+    private HttpServletRequest wrapped(final HttpServletRequest request) {
+        return new RequestWrapper(request);
+    }
+
+    private HttpServletResponse wrapped(final HttpServletResponse response) {
+        return new ResponseWrapper(response);
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider b/tests/integration/servlet-request-wrapper-binding/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
new file mode 100644
index 0000000..fd57f7c
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/main/resources/META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding.RequestResponseWrapperProvider
\ No newline at end of file
diff --git a/tests/integration/servlet-request-wrapper-binding/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-request-wrapper-binding/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..0cee595
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <servlet>
+        <servlet-name>jerseyApp</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding.RequestResponseInjectedResource</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>jerseyApp</servlet-name>
+        <url-pattern>/webxmlconfigured/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/AbstractRequestResponseTypeTest.java b/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/AbstractRequestResponseTypeTest.java
new file mode 100644
index 0000000..4f4710d
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/AbstractRequestResponseTypeTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding.RequestResponseWrapperProvider;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import org.junit.Test;
+
+/**
+ * Make sure that injected request/response instances
+ * are of the types injected by {@link RequestResponseWrapperProvider}.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public abstract class AbstractRequestResponseTypeTest extends JerseyTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testRequestType() throws Exception {
+        final String requestType = target(getAppBasePath()).path("requestType").request().get(String.class);
+        assertThat(requestType, is(equalTo(RequestResponseWrapperProvider.RequestWrapper.class.getName())));
+    }
+
+    @Test
+    public void testResponseType() throws Exception {
+        final String requestType = target(getAppBasePath()).path("responseType").request().get(String.class);
+        assertThat(requestType, is(equalTo(RequestResponseWrapperProvider.ResponseWrapper.class.getName())));
+    }
+
+    protected abstract String getAppBasePath();
+}
diff --git a/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/AutodetectedAppRequestResponseTypeITCase.java b/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/AutodetectedAppRequestResponseTypeITCase.java
new file mode 100644
index 0000000..7b7403a
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/AutodetectedAppRequestResponseTypeITCase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding.JaxRsApplicationAutodetected;
+
+/**
+ * Test for the autodetected JAX-RS app.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class AutodetectedAppRequestResponseTypeITCase extends AbstractRequestResponseTypeTest {
+
+    @Override
+    protected String getAppBasePath() {
+        return "autodetected";
+    }
+
+    @Override
+    protected Application configure() {
+        return ResourceConfig.forApplicationClass(JaxRsApplicationAutodetected.class);
+    }
+}
diff --git a/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/WebXmlConfiguredAppRequestResponseTypeITCase.java b/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/WebXmlConfiguredAppRequestResponseTypeITCase.java
new file mode 100644
index 0000000..f6a85ad
--- /dev/null
+++ b/tests/integration/servlet-request-wrapper-binding/src/test/java/org/glassfish/jersey/tests/integration/servlet_request_wrapper/WebXmlConfiguredAppRequestResponseTypeITCase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlet_request_wrapper;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.tests.integration.servlet_request_wrapper_binding.RequestResponseInjectedResource;
+
+/**
+ * Test for the web.xml configured JAX-RS app.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class WebXmlConfiguredAppRequestResponseTypeITCase extends AbstractRequestResponseTypeTest {
+
+    @Override
+    protected String getAppBasePath() {
+        return "webxmlconfigured";
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(RequestResponseInjectedResource.class);
+    }
+}
diff --git a/tests/integration/servlet-tests/pom.xml b/tests/integration/servlet-tests/pom.xml
new file mode 100644
index 0000000..015852d
--- /dev/null
+++ b/tests/integration/servlet-tests/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>servlet-tests</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-servlet-tests</name>
+
+    <description>Set of smaller unrelated servlet-specific tests.</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+              <groupId>org.mortbay.jetty</groupId>
+              <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/CacheControlOn404Resource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/CacheControlOn404Resource.java
new file mode 100644
index 0000000..4fca464
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/CacheControlOn404Resource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author Martin Matula
+ */
+@Path("404")
+public class CacheControlOn404Resource {
+    @GET
+    public Response get404() {
+        CacheControl cc = new CacheControl();
+        cc.setMaxAge(10);
+        return Response.status(Response.Status.NOT_FOUND).cacheControl(cc).type("text/plain").entity("404 Not Found").build();
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/CustomMediaTypeAnd404Resource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/CustomMediaTypeAnd404Resource.java
new file mode 100644
index 0000000..08d2952
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/CustomMediaTypeAnd404Resource.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ *
+ * @author Miroslav Fuksa
+ */
+@Path("resource404")
+public class CustomMediaTypeAnd404Resource {
+    @Path("content-type-entity")
+    @GET
+    public Response getSpecialContentType() {
+        return Response.status(Response.Status.NOT_FOUND).type("application/something").entity("not found custom entity").build();
+    }
+
+    @Path("content-type-empty-entity")
+    @GET
+    public Response getSpecialContentTypeWithEmptyEntityString() {
+        return Response.status(Response.Status.NOT_FOUND).type("application/something").entity("").build();
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathResource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathResource.java
new file mode 100644
index 0000000..11b5c2a
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Martin Matula
+ */
+@Path("contextPathResource")
+public class FilterContextPathResource {
+    @GET
+    public String get() {
+        return "contextPathResource";
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterForwardOn404Resource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterForwardOn404Resource.java
new file mode 100644
index 0000000..316b007
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterForwardOn404Resource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Martin Matula
+ */
+@Path("forwardingFilter/resource")
+public class FilterForwardOn404Resource {
+    @GET
+    public String get() {
+        return "forwardingFilter/resource";
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterNoContextPathResource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterNoContextPathResource.java
new file mode 100644
index 0000000..739a2d0
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterNoContextPathResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Martin Matula
+ */
+@Path("filter/resource")
+public class FilterNoContextPathResource {
+    @GET
+    public String get() {
+        return "filter/resource";
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterStaticContentResource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterStaticContentResource.java
new file mode 100644
index 0000000..256ab41
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FilterStaticContentResource.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Martin Matula
+ */
+@Path("staticContentFilter/resource")
+public class FilterStaticContentResource {
+    @GET
+    public String get() {
+        return "staticContentFilter/resource";
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionFilter.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionFilter.java
new file mode 100644
index 0000000..bda0d3a
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * @author Martin Matula
+ */
+public class FormConsumptionFilter implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+            throws IOException, ServletException {
+        // consume entity
+        servletRequest.getParameter("text");
+        filterChain.doFilter(servletRequest, servletResponse);
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionResource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionResource.java
new file mode 100644
index 0000000..f0910fd
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionResource.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Martin Matula
+ */
+@Path("form-consumption")
+public class FormConsumptionResource {
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public String postIt(@FormParam("text") String text) {
+        return text;
+    }
+
+
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Path("encoding")
+    public String postEncoding(@Encoded @FormParam("text") String text) throws UnsupportedEncodingException {
+        return URLDecoder.decode(text, "UTF-8");
+    }
+
+    @PUT
+    @Produces(MediaType.TEXT_PLAIN)
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public String putIt(@FormParam("text") String text) {
+        return text;
+    }
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/SecuredResource.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/SecuredResource.java
new file mode 100644
index 0000000..6137675
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/SecuredResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlettests;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import javax.annotation.security.RolesAllowed;
+
+/**
+ * @author Petr Bouda
+ */
+@Path("admin")
+public class SecuredResource {
+
+    @GET
+    @RolesAllowed("admin")
+    public String secured() {
+        return "Secured";
+    }
+
+}
diff --git a/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/SuppressContentLengthFilter.java b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/SuppressContentLengthFilter.java
new file mode 100644
index 0000000..abb2ac6
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/java/org/glassfish/jersey/tests/integration/servlettests/SuppressContentLengthFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlettests;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * JERSEY-2936 reproducer filter.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class SuppressContentLengthFilter implements Filter {
+
+    public static final String PARAMETER_NAME_SUPPRESS_CONTENT_LENGTH = "SuppressContentLength";
+
+    public void doFilter(final ServletRequest request, ServletResponse response, final FilterChain chain)
+            throws IOException, ServletException {
+        if (Boolean.parseBoolean(request.getParameter(PARAMETER_NAME_SUPPRESS_CONTENT_LENGTH))) {
+            response = new HttpServletResponseWrapper((HttpServletResponse) response) {
+                @Override
+                public void setContentLength(int len) {
+                    // do not delegate to original ServletResponse -> response is NOT committed
+                }
+            };
+        }
+        chain.doFilter(request, response);
+    }
+
+    public void init(FilterConfig filterConfig) {
+        //NOOP
+    }
+
+    public void destroy() {
+        //NOOP
+    }
+
+}
diff --git a/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml b/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5d9d99e
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <!-- servlet for generic use (has all resources) -->
+    <servlet>
+        <servlet-name>testServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>testServlet</servlet-name>
+        <url-pattern>/servlet/*</url-pattern>
+    </servlet-mapping>
+
+    <!-- filter to test NOT commited responses -->
+    <filter>
+        <filter-name>SuppressContentLengthFilter</filter-name>
+        <filter-class>org.glassfish.jersey.tests.integration.servlettests.SuppressContentLengthFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>SuppressContentLengthFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <!-- filter for generic use (has all resources) -->
+    <filter>
+        <filter-name>testFilter</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testFilter</filter-name>
+        <url-pattern>/filter/*</url-pattern>
+    </filter-mapping>
+
+    <!-- filter context path -->
+    <filter>
+        <filter-name>testFilter2</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests.FilterContextPathResource
+                org.glassfish.jersey.tests.integration.servlettests.FilterNoContextPathResource</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.filter.contextPath</param-name>
+            <param-value>/contextPathFilter/</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testFilter2</filter-name>
+        <url-pattern>/contextPathFilter/*</url-pattern>
+    </filter-mapping>
+
+    <!-- forward on 404 -->
+    <filter>
+        <filter-name>testFilter3</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests.FilterForwardOn404Resource</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.filter.forwardOn404</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testFilter3</filter-name>
+        <url-pattern>/forwardingFilter/*</url-pattern>
+    </filter-mapping>
+
+    <!-- static content regex -->
+    <filter>
+        <filter-name>testFilter4</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests.FilterStaticContentResource</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.filter.staticContentRegex</param-name>
+            <param-value>/staticContentFilter/.*\.jsp</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>testFilter4</filter-name>
+        <url-pattern>/staticContentFilter/*</url-pattern>
+    </filter-mapping>
+
+    <!-- form consumption -->
+    <filter>
+        <filter-name>FormConsumptionFilter</filter-name>
+        <filter-class>org.glassfish.jersey.tests.integration.servlettests.FormConsumptionFilter</filter-class>
+    </filter>
+    <servlet>
+        <servlet-name>FormConsumptionServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests.FormConsumptionResource</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>FormConsumptionServlet</servlet-name>
+        <url-pattern>/form-consumption/*</url-pattern>
+    </servlet-mapping>
+    <filter-mapping>
+        <filter-name>FormConsumptionFilter</filter-name>
+        <url-pattern>/form-consumption/*</url-pattern>
+    </filter-mapping>
+
+    <!-- 404 -->
+    <filter>
+        <filter-name>custom404</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.servlettests.CustomMediaTypeAnd404Resource</param-value>
+        </init-param>
+        <init-param>
+            <param-name>jersey.config.servlet.filter.contextPath</param-name>
+            <param-value>/custom404/</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>custom404</filter-name>
+        <url-pattern>/custom404/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/integration/servlet-tests/src/main/webapp/filter/index.jsp b/tests/integration/servlet-tests/src/main/webapp/filter/index.jsp
new file mode 100644
index 0000000..6017d94
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/webapp/filter/index.jsp
@@ -0,0 +1,27 @@
+<%--
+
+    Copyright (c) 2012, 2018 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
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title></title>
+</head>
+<body>
+
+</body>
+</html>
diff --git a/tests/integration/servlet-tests/src/main/webapp/forwardingFilter/index.jsp b/tests/integration/servlet-tests/src/main/webapp/forwardingFilter/index.jsp
new file mode 100644
index 0000000..6017d94
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/webapp/forwardingFilter/index.jsp
@@ -0,0 +1,27 @@
+<%--
+
+    Copyright (c) 2012, 2018 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
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title></title>
+</head>
+<body>
+
+</body>
+</html>
diff --git a/tests/integration/servlet-tests/src/main/webapp/staticContentFilter/index.jsp b/tests/integration/servlet-tests/src/main/webapp/staticContentFilter/index.jsp
new file mode 100644
index 0000000..6017d94
--- /dev/null
+++ b/tests/integration/servlet-tests/src/main/webapp/staticContentFilter/index.jsp
@@ -0,0 +1,27 @@
+<%--
+
+    Copyright (c) 2012, 2018 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
+
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title></title>
+</head>
+<body>
+
+</body>
+</html>
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/CacheControlOn404ITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/CacheControlOn404ITCase.java
new file mode 100644
index 0000000..5697e39
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/CacheControlOn404ITCase.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class related to issue JERSEY-1189.
+ * Confirms that if one sends an entity with the error status, the cache control
+ * headers don't get reset by the container.
+ *
+ * @author Martin Matula
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class CacheControlOn404ITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(CacheControlOn404Resource.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void test404() throws Exception {
+        test404Impl(false);
+    }
+
+    @Test
+    public void test404SuppressContentLength() throws Exception {
+        test404Impl(true);
+    }
+
+    private void test404Impl(final boolean suppressContentLength) {
+        Response r = target("servlet").path("404")
+                .queryParam(SuppressContentLengthFilter.PARAMETER_NAME_SUPPRESS_CONTENT_LENGTH, suppressContentLength)
+                .request().get();
+        assertEquals(404, r.getStatus());
+        assertEquals("404 Not Found", r.readEntity(String.class));
+        final String[] values = r.getHeaderString(HttpHeaders.CACHE_CONTROL).split(",");
+        assertEquals(2, values.length);
+        assertEquals("no-transform", values[0].trim());
+        assertEquals("max-age=10", values[1].trim());
+    }
+
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/Custom404MediaTypeITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/Custom404MediaTypeITCase.java
new file mode 100644
index 0000000..619d5ba
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/Custom404MediaTypeITCase.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.servlettests;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Miroslav Fuksa
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Custom404MediaTypeITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testCustom404() {
+        testCustom404Impl(false);
+    }
+
+    @Test
+    public void testCustom404WithEmtpyEntityString() {
+        testCustom404WithEmtpyEntityStringImpl(false);
+    }
+
+    @Test
+    public void testCustom404SuppressContentLength() {
+        testCustom404Impl(true);
+    }
+
+    @Test
+    public void testCustom404WithEmtpyEntityStringSuppressContentLength() {
+        testCustom404WithEmtpyEntityStringImpl(true);
+    }
+
+    private void testCustom404Impl(final boolean suppressContentLength) {
+        final Response response = target().path("custom404/resource404/content-type-entity")
+                .queryParam(SuppressContentLengthFilter.PARAMETER_NAME_SUPPRESS_CONTENT_LENGTH, suppressContentLength)
+                .request()
+                .get();
+        Assert.assertEquals(404, response.getStatus());
+        Assert.assertEquals("application/something", response.getMediaType().toString());
+        Assert.assertEquals("not found custom entity", response.readEntity(String.class));
+    }
+
+    private void testCustom404WithEmtpyEntityStringImpl(final boolean suppressContentLength) {
+        final Response response = target().path("custom404/resource404/content-type-empty-entity")
+                .queryParam(SuppressContentLengthFilter.PARAMETER_NAME_SUPPRESS_CONTENT_LENGTH, suppressContentLength)
+                .request().get();
+        Assert.assertEquals(404, response.getStatus());
+        Assert.assertEquals("application/something", response.getMediaType().toString());
+        Assert.assertEquals("", response.readEntity(String.class));
+    }
+
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/DuplicateHeaderITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/DuplicateHeaderITCase.java
new file mode 100644
index 0000000..2856ecb
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/DuplicateHeaderITCase.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlettests;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class DuplicateHeaderITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testDuplicateHeader() throws IOException {
+        testDuplicateHeaderImpl("contextPathFilter/contextPathResource");
+        testDuplicateHeaderImpl("servlet/contextPathResource");
+    }
+
+    private void testDuplicateHeaderImpl(final String path) throws IOException {
+        testDuplicateHeaderImpl(0, HttpURLConnection.HTTP_OK, path);
+        testDuplicateHeaderImpl(1, HttpURLConnection.HTTP_OK, path);
+        testDuplicateHeaderImpl(2, HttpURLConnection.HTTP_BAD_REQUEST, path);
+    }
+
+    private void testDuplicateHeaderImpl(final int headerCount, int expectedResponseCode, final String path)
+            throws IOException {
+        final String headerName = HttpHeaders.CONTENT_TYPE;
+        URL getUrl = UriBuilder.fromUri(getBaseUri()).path(path).build().toURL();
+        HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
+        try {
+            connection.setRequestMethod("GET");
+            for (int i = 0; i < headerCount; i++) {
+                connection.addRequestProperty(headerName, "N/A");
+            }
+            connection.connect();
+            assertEquals(path + " [" + headerName + ":" + headerCount + "x]", expectedResponseCode, connection.getResponseCode());
+        } finally {
+            connection.disconnect();
+        }
+    }
+
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java
new file mode 100644
index 0000000..60e99d3
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class FilterContextPathITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testContextPathResource() {
+        assertEquals("contextPathResource", target("contextPathFilter/contextPathResource").request().get(String.class));
+    }
+
+    @Test
+    public void testNoContextPathResource() {
+        assertEquals("filter/resource", target("filter/resource").request().get(String.class));
+    }
+
+    @Test
+    public void testOtherResourceUnreachable() {
+        Response r = target("filter/contextPathResource").request().get();
+        assertEquals(404, r.getStatus());
+    }
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionITCase.java
new file mode 100644
index 0000000..af7a5f9
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FormConsumptionITCase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Form;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class FormConsumptionITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testPut() {
+        Form form = new Form();
+        form.param("text", "this is a test");
+        String result = target("form-consumption/form-consumption").request().put(Entity.form(form), String.class);
+        assertEquals(form.asMap().getFirst("text"), result);
+    }
+
+    @Test
+    public void testPost() {
+        Form form = new Form();
+        form.param("text", "this is a test");
+        String result = target("form-consumption/form-consumption").request().post(Entity.form(form), String.class);
+        assertEquals(form.asMap().getFirst("text"), result);
+    }
+
+    @Test
+    public void testPostWithEncoding() {
+        Form form = new Form();
+        form.param("text", "this is an encoding test +-*/=");
+        String result = target("form-consumption/form-consumption/encoding").request().post(Entity.form(form), String.class);
+        assertEquals(form.asMap().getFirst("text"), result);
+    }
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java
new file mode 100644
index 0000000..1bc99e9
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class ForwardOn404ITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testResourceReachable() {
+        assertEquals("forwardingFilter/resource", target("forwardingFilter/resource").request().get(String.class));
+    }
+
+    @Test
+    public void testIndexReachable() {
+        Response r = target("forwardingFilter/index.jsp").request().get();
+        assertEquals(200, r.getStatus());
+    }
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/InvalidRequestUriITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/InvalidRequestUriITCase.java
new file mode 100644
index 0000000..db67e95
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/InvalidRequestUriITCase.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlettests;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class related to issue JERSEY-2680.
+ *
+ * @author Michal Gajdos
+ */
+public class InvalidRequestUriITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testInvalidRequestUriFilter() throws Exception {
+        invalidRequestUri("filter");
+    }
+
+    @Test
+    public void testInvalidRequestUriServlet() throws Exception {
+        invalidRequestUri("servlet");
+    }
+
+    public void invalidRequestUri(final String path) throws Exception {
+        final URL url = new URL(getBaseUri().toString() + path + "/resource{");
+        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+        connection.setRequestMethod("GET");
+        connection.setRequestProperty("Accept", "text/plain");
+        connection.connect();
+
+        assertEquals(400, connection.getResponseCode());
+    }
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/RolesAllowedFilterITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/RolesAllowedFilterITCase.java
new file mode 100644
index 0000000..a22f0c8
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/RolesAllowedFilterITCase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.servlettests;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Petr Bouda
+ */
+public class RolesAllowedFilterITCase extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        // Dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void test403() throws Exception {
+        Response r = target("servlet/admin").request().get();
+        assertEquals(403, r.getStatus());
+    }
+}
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java
new file mode 100644
index 0000000..70647e2
--- /dev/null
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2012, 2018 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.integration.servlettests;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Martin Matula
+ */
+public class StaticContentRegexITCase extends JerseyTest {
+    @Override
+    protected Application configure() {
+        // dummy resource config
+        return new ResourceConfig();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testIndexUnreachable() {
+        Response r = target("filter").path("index.jsp").request().get();
+        assertEquals(404, r.getStatus());
+    }
+
+    @Test
+    public void testIndexReachable() {
+        Response r = target("staticContentFilter/index.jsp").request().get();
+        assertEquals(200, r.getStatus());
+    }
+
+    @Test
+    public void testResourceReachable() {
+        Response r = target("staticContentFilter/resource").request().get();
+        assertEquals(200, r.getStatus());
+        assertEquals("staticContentFilter/resource", r.readEntity(String.class));
+    }
+}
diff --git a/tests/integration/sonar-test/pom.xml b/tests/integration/sonar-test/pom.xml
new file mode 100644
index 0000000..b897be8
--- /dev/null
+++ b/tests/integration/sonar-test/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>sonar-test</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-sonar-test</name>
+
+    <description>
+        The purpose of this module is to provide an ability to check whether Sonar's code coverage includes
+        lines of code executed from an externally executed JVM, Jetty web server in this case.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/integration/sonar-test/src/main/java/org/glassfish/jersey/tests/integration/sonar/TestApplication.java b/tests/integration/sonar-test/src/main/java/org/glassfish/jersey/tests/integration/sonar/TestApplication.java
new file mode 100644
index 0000000..d5b67e7
--- /dev/null
+++ b/tests/integration/sonar-test/src/main/java/org/glassfish/jersey/tests/integration/sonar/TestApplication.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.sonar;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * Jersey application for code coverage testing.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@ApplicationPath("/")
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(TestResource.class);
+    }
+}
diff --git a/tests/integration/sonar-test/src/main/java/org/glassfish/jersey/tests/integration/sonar/TestResource.java b/tests/integration/sonar-test/src/main/java/org/glassfish/jersey/tests/integration/sonar/TestResource.java
new file mode 100644
index 0000000..b8bfaab
--- /dev/null
+++ b/tests/integration/sonar-test/src/main/java/org/glassfish/jersey/tests/integration/sonar/TestResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.sonar;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.glassfish.jersey.internal.sonar.SonarJerseyCommon;
+import org.glassfish.jersey.server.internal.sonar.SonarJerseyServer;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/")
+@Produces("text/plain")
+public class TestResource {
+
+    @GET
+    @Path("test")
+    public String helloServer() {
+        return new SonarJerseyCommon().integrationServerJvm() + " " + new SonarJerseyServer().integrationServerJvm();
+    }
+
+}
diff --git a/tests/integration/sonar-test/src/main/webapp/WEB-INF/web.xml b/tests/integration/sonar-test/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..ab6014a
--- /dev/null
+++ b/tests/integration/sonar-test/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>jersey</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.integration.sonar.TestApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>jersey</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/integration/sonar-test/src/test/java/org/glassfish/jersey/tests/integration/sonar/JerseySonarITCase.java b/tests/integration/sonar-test/src/test/java/org/glassfish/jersey/tests/integration/sonar/JerseySonarITCase.java
new file mode 100644
index 0000000..1c9f078
--- /dev/null
+++ b/tests/integration/sonar-test/src/test/java/org/glassfish/jersey/tests/integration/sonar/JerseySonarITCase.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.sonar;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.internal.sonar.SonarJerseyCommon;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.internal.sonar.SonarJerseyServer;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class JerseySonarITCase extends JerseyTest {
+
+    @Test
+    public void testIntegrationServerJvm() {
+        final String string = target("test").request().get(String.class);
+
+        Assert.assertEquals("common server jvm server server jvm", string);
+    }
+
+    @Test
+    public void testIntegrationTestJvm() {
+        final String string = new SonarJerseyCommon().integrationTestJvm() + " " + new SonarJerseyServer().integrationTestJvm();
+
+        Assert.assertEquals("common test jvm server test jvm", string);
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestApplication.class);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+}
diff --git a/tests/integration/spring4/README.txt b/tests/integration/spring4/README.txt
new file mode 100644
index 0000000..9a105f7
--- /dev/null
+++ b/tests/integration/spring4/README.txt
@@ -0,0 +1,30 @@
+
+tests
+=====
+
+Tests are located in jersey-spring-test module.
+The module contains a test webapp and test code.
+The tests can be run in Jersey test container or an external container.
+
+- Running tests in Jersey test container
+    mvn clean test
+
+- Running tests in an external container
+  build the test app
+  deploy to an external container
+  configure container connection info in jersey-spring-test/pom.xml, if needed
+  run tests in integration test mode:
+    mvn -Pit verify
+
+- Running tests in embedded Jetty instance
+  build the test app
+  deploy to Jetty:
+    mvn -Pjetty jetty:run
+  run tests in integration test mode in another console session:
+    mvn -Pit verify
+
+test class naming conventions
+- *ITTest.java: run in unit and IT test mode
+- *Test.java: run as unit tests
+- *IT.java: run as IT tests
+
diff --git a/tests/integration/spring4/pom.xml b/tests/integration/spring4/pom.xml
new file mode 100644
index 0000000..c5e93ab
--- /dev/null
+++ b/tests/integration/spring4/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.26-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>spring4</artifactId>
+
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-spring4</name>
+
+    <description>
+        Jersey tests for Spring 4 integration
+    </description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>${servlet2.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-spring4</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <webApp>
+                        <contextPath>/</contextPath>
+                        <webInfIncludeJarPattern>.*\.jar$</webInfIncludeJarPattern>
+                    </webApp>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>delayed-strategy-skip-test</id>
+            <activation>
+                <property>
+                    <name>org.glassfish.jersey.injection.manager.strategy</name>
+                    <value>delayed</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <configuration>
+                            <skipTests>true</skipTests>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountJerseyResource.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountJerseyResource.java
new file mode 100644
index 0000000..80af7bb
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountJerseyResource.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import java.math.BigDecimal;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+/**
+ * Jersey managed JAX-RS resource for testing jersey-spring.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+@Path("/jersey/account")
+public class AccountJerseyResource {
+
+    @Inject
+    @Named("AccountService-singleton")
+    private AccountService accountServiceInject;
+
+    @Autowired
+    @Qualifier("AccountService-singleton")
+    private AccountService accountServiceAutowired;
+
+    @Inject
+    @Named("AccountService-request-1")
+    private AccountService accountServiceRequest1;
+
+    @Autowired
+    @Qualifier("AccountService-request-1")
+    private AccountService accountServiceRequest2;
+
+    @Autowired
+    @Qualifier("AccountService-prototype-1")
+    private AccountService accountServicePrototype1;
+
+    @Autowired
+    @Qualifier("AccountService-prototype-1")
+    private AccountService accountServicePrototype2;
+
+    @Autowired
+    private HttpServletRequest httpServletRequest;
+
+    @Inject
+    private HK2ServiceSingleton hk2Singleton;
+
+    @Inject
+    private HK2ServiceRequestScoped hk2RequestScoped;
+
+    @Inject
+    private HK2ServicePerLookup hk2PerLookup;
+
+    private String message = "n/a";
+
+    // resource methods for testing resource class scope
+    @GET
+    @Path("message")
+    public String getMessage() {
+        return message;
+    }
+
+    @PUT
+    @Path("message")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public String setMessage(final String message) {
+        this.message = message;
+        return message;
+    }
+
+    // JERSEY-2506 FIX VERIFICATION
+    @GET
+    @Path("server")
+    public String verifyServletRequestInjection() {
+        return "PASSED: " + httpServletRequest.getServerName();
+    }
+
+    @GET
+    @Path("singleton/server")
+    public String verifyServletRequestInjectionIntoSingleton() {
+        return accountServiceInject.verifyServletRequestInjection();
+    }
+
+    @GET
+    @Path("singleton/autowired/server")
+    public String verifyServletRequestInjectionIntoAutowiredSingleton() {
+        return accountServiceAutowired.verifyServletRequestInjection();
+    }
+
+    @GET
+    @Path("request/server")
+    public String verifyServletRequestInjectionIntoRequestScopedBean() {
+        return accountServiceRequest1.verifyServletRequestInjection();
+    }
+
+    @GET
+    @Path("prototype/server")
+    public String verifyServletRequestInjectionIntoPrototypeScopedBean() {
+        return accountServicePrototype1.verifyServletRequestInjection();
+    }
+
+    // resource methods for testing singleton scoped beans
+    @GET
+    @Path("singleton/inject/{accountId}")
+    public BigDecimal getAccountBalanceSingletonInject(@PathParam("accountId") final String accountId) {
+        return accountServiceInject.getAccountBalance(accountId);
+    }
+
+    @GET
+    @Path("singleton/autowired/{accountId}")
+    public BigDecimal getAccountBalanceSingletonAutowired(@PathParam("accountId") final String accountId) {
+        return accountServiceAutowired.getAccountBalance(accountId);
+    }
+
+    @PUT
+    @Path("singleton/{accountId}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public void setAccountBalanceSingleton(@PathParam("accountId") final String accountId, final String balance) {
+        accountServiceInject.setAccountBalance(accountId, new BigDecimal(balance));
+    }
+
+    // resource methods for testing request scoped beans
+    @PUT
+    @Path("request/{accountId}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public BigDecimal setAccountBalanceRequest(@PathParam("accountId") final String accountId, final String balance) {
+        accountServiceRequest1.setAccountBalance(accountId, new BigDecimal(balance));
+        return accountServiceRequest2.getAccountBalance(accountId);
+    }
+
+    // resource methods for testing prototype scoped beans
+    @PUT
+    @Path("prototype/{accountId}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public BigDecimal setAccountBalancePrototype(@PathParam("accountId") final String accountId, final String balance) {
+        accountServicePrototype1.setAccountBalance(accountId, new BigDecimal(balance));
+        return accountServicePrototype2.getAccountBalance(accountId);
+    }
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountService.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountService.java
new file mode 100644
index 0000000..851e215
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import java.math.BigDecimal;
+
+/**
+ * Simple account service to testify injection into different scopes.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public interface AccountService {
+
+    void setAccountBalance(String accountId, BigDecimal balance);
+
+    BigDecimal getAccountBalance(String accountId);
+
+    String verifyServletRequestInjection();
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountServiceImpl.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountServiceImpl.java
new file mode 100644
index 0000000..8b99e62
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountServiceImpl.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * AccountService implementation.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class AccountServiceImpl implements AccountService {
+
+    private Map<String, BigDecimal> accounts = new HashMap<>();
+    private BigDecimal defaultAccountBalance;
+
+    // JERSEY-2506 FIX VERIFICATION
+    @Autowired
+    private HttpServletRequest httpServletRequest;
+
+    @Override
+    public void setAccountBalance(String accountId, BigDecimal balance) {
+        accounts.put(accountId, balance);
+    }
+
+    @Override
+    public BigDecimal getAccountBalance(String accountId) {
+        BigDecimal balance = accounts.get(accountId);
+        if (balance == null) {
+            return defaultAccountBalance;
+        }
+        return balance;
+    }
+
+    public void setDefaultAccountBalance(String defaultAccountBalance) {
+        this.defaultAccountBalance = new BigDecimal(defaultAccountBalance);
+    }
+
+    public String verifyServletRequestInjection() {
+        return "PASSED: " + httpServletRequest.getServerName();
+    }
+
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountSpringResource.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountSpringResource.java
new file mode 100644
index 0000000..6a6e6c2
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/AccountSpringResource.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import java.math.BigDecimal;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring managed JAX-RS resource for testing jersey-spring.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+@Path("/spring/account")
+@Component
+public class AccountSpringResource {
+
+    @Inject
+    @Named("AccountService-singleton")
+    private AccountService accountServiceInject;
+
+    @Autowired
+    @Qualifier("AccountService-singleton")
+    private AccountService accountServiceAutowired;
+
+    @Inject
+    @Named("AccountService-request-1")
+    private AccountService accountServiceRequest1;
+
+    @Autowired
+    @Qualifier("AccountService-request-1")
+    private AccountService accountServiceRequest2;
+
+    @Autowired
+    @Qualifier("AccountService-prototype-1")
+    private AccountService accountServicePrototype1;
+
+    @Autowired
+    @Qualifier("AccountService-prototype-1")
+    private AccountService accountServicePrototype2;
+
+    @Autowired
+    private HttpServletRequest httpServletRequest;
+
+    @Inject
+    private HK2ServiceSingleton hk2Singleton;
+
+    @Inject
+    private HK2ServiceRequestScoped hk2RequestScoped;
+
+    @Inject
+    private HK2ServicePerLookup hk2PerLookup;
+
+    private String message = "n/a";
+
+    // resource methods for testing resource class scope
+    @GET
+    @Path("message")
+    public String getMessage() {
+        return message;
+    }
+
+    @PUT
+    @Path("message")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public String setMessage(String message) {
+        this.message = message;
+        return message;
+    }
+
+    // JERSEY-2506 FIX VERIFICATION
+    @GET
+    @Path("server")
+    public String verifyServletRequestInjection() {
+        return "PASSED: " + httpServletRequest.getServerName();
+    }
+
+    @GET
+    @Path("singleton/server")
+    public String verifyServletRequestInjectionIntoSingleton() {
+        return accountServiceInject.verifyServletRequestInjection();
+    }
+
+    @GET
+    @Path("singleton/autowired/server")
+    public String verifyServletRequestInjectionIntoAutowiredSingleton() {
+        return accountServiceAutowired.verifyServletRequestInjection();
+    }
+
+    @GET
+    @Path("request/server")
+    public String verifyServletRequestInjectionIntoRequestScopedBean() {
+        return accountServiceRequest1.verifyServletRequestInjection();
+    }
+
+    @GET
+    @Path("prototype/server")
+    public String verifyServletRequestInjectionIntoPrototypeScopedBean() {
+        return accountServicePrototype1.verifyServletRequestInjection();
+    }
+
+    // resource methods for testing singleton scoped beans
+    @GET
+    @Path("singleton/inject/{accountId}")
+    public BigDecimal getAccountBalanceSingletonInject(@PathParam("accountId") String accountId) {
+        return accountServiceInject.getAccountBalance(accountId);
+    }
+
+    @GET
+    @Path("singleton/autowired/{accountId}")
+    public BigDecimal getAccountBalanceSingletonAutowired(@PathParam("accountId") String accountId) {
+        return accountServiceAutowired.getAccountBalance(accountId);
+    }
+
+    @PUT
+    @Path("singleton/{accountId}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public void setAccountBalanceSingleton(@PathParam("accountId") String accountId, String balance) {
+        accountServiceInject.setAccountBalance(accountId, new BigDecimal(balance));
+    }
+
+    // resource methods for testing request scoped beans
+    @PUT
+    @Path("request/{accountId}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public BigDecimal setAccountBalanceRequest(@PathParam("accountId") String accountId, String balance) {
+        accountServiceRequest1.setAccountBalance(accountId, new BigDecimal(balance));
+        return accountServiceRequest2.getAccountBalance(accountId);
+    }
+
+    // resource methods for testing prototype scoped beans
+    @PUT
+    @Path("prototype/{accountId}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public BigDecimal setAccountBalancePrototype(@PathParam("accountId") String accountId, String balance) {
+        accountServicePrototype1.setAccountBalance(accountId, new BigDecimal(balance));
+        return accountServicePrototype2.getAccountBalance(accountId);
+    }
+
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/ControllerResource.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/ControllerResource.java
new file mode 100644
index 0000000..7714c22
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/ControllerResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.stereotype.Controller;
+
+/**
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+@Controller
+@Path("/spring/controller")
+public class ControllerResource {
+
+    private String message;
+
+    @PUT
+    @Path("message")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public String setMessage(final String message) {
+        this.message = message;
+        return message;
+    }
+
+    @GET
+    @Path("message")
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/Endpoint.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/Endpoint.java
new file mode 100644
index 0000000..da9f170
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/Endpoint.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Component
+public @interface Endpoint {
+
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/EndpointResource.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/EndpointResource.java
new file mode 100644
index 0000000..9af0858
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/EndpointResource.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+@Endpoint
+@Path("/spring/endpoint")
+public class EndpointResource {
+
+    private String message;
+
+    @PUT
+    @Path("message")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public String setMessage(final String message) {
+        this.message = message;
+        return message;
+    }
+
+    @GET
+    @Path("message")
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServicePerLookup.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServicePerLookup.java
new file mode 100644
index 0000000..ff689c6
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServicePerLookup.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+/**
+ * Type to be handled as HK2 per-lookup bean.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class HK2ServicePerLookup {
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServiceRequestScoped.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServiceRequestScoped.java
new file mode 100644
index 0000000..da45aea
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServiceRequestScoped.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+/**
+ * Type to be handled as HK2 request scoped bean.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class HK2ServiceRequestScoped {
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServiceSingleton.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServiceSingleton.java
new file mode 100644
index 0000000..a7b8fa8
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/HK2ServiceSingleton.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+/**
+ * Type to be handled as HK2 singleton.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class HK2ServiceSingleton {
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/MyApplication.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/MyApplication.java
new file mode 100644
index 0000000..568f30f
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/MyApplication.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.core.Application;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.Binder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.PerLookup;
+import org.glassfish.jersey.process.internal.RequestScoped;
+
+/**
+ * JAX-RS application class for configuring injectable services in HK2 registry for testing purposes.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class MyApplication extends Application {
+
+    @Inject
+    public MyApplication(final InjectionManager injectionManager) {
+        Binder binder = new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bindAsContract(HK2ServiceSingleton.class).in(Singleton.class);
+                bindAsContract(HK2ServiceRequestScoped.class).in(RequestScoped.class);
+                bindAsContract(HK2ServicePerLookup.class).in(PerLookup.class);
+            }
+        };
+
+        injectionManager.register(binder);
+    }
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/RepositoryResource.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/RepositoryResource.java
new file mode 100644
index 0000000..8c3b563
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/RepositoryResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+@Repository
+@Path("/spring/repository")
+public class RepositoryResource {
+
+    private String message;
+
+    @PUT
+    @Path("message")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public String setMessage(final String message) {
+        this.message = message;
+        return message;
+    }
+
+    @GET
+    @Path("message")
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/ServiceResource.java b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/ServiceResource.java
new file mode 100644
index 0000000..3516dcf
--- /dev/null
+++ b/tests/integration/spring4/src/main/java/org/glassfish/jersey/server/spring/test/ServiceResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+@Service
+@Path("/spring/service")
+public class ServiceResource {
+
+    private String message;
+
+    @PUT
+    @Path("message")
+    @Consumes(MediaType.TEXT_PLAIN)
+    public String setMessage(final String message) {
+        this.message = message;
+        return message;
+    }
+
+    @GET
+    @Path("message")
+    public String getMessage() {
+        return message;
+    }
+}
diff --git a/tests/integration/spring4/src/main/resources/applicationContext.xml b/tests/integration/spring4/src/main/resources/applicationContext.xml
new file mode 100644
index 0000000..0cc9127
--- /dev/null
+++ b/tests/integration/spring4/src/main/resources/applicationContext.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop
+        http://www.springframework.org/schema/aop/spring-aop.xsd">
+
+    <!--<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>-->
+    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
+        <property name="autowiredAnnotationTypes">
+            <set>
+                <value>org.springframework.beans.factory.annotation.Autowired</value>
+                <value>org.springframework.beans.factory.annotation.Value</value>
+            </set>
+        </property>
+    </bean>
+
+    <bean name="AccountService-singleton" class="org.glassfish.jersey.server.spring.test.AccountServiceImpl" />
+
+    <bean name="AccountService-request-1" class="org.glassfish.jersey.server.spring.test.AccountServiceImpl" scope="request">
+        <aop:scoped-proxy/>
+    </bean>
+
+    <bean name="AccountService-request-2" class="org.glassfish.jersey.server.spring.test.AccountServiceImpl" scope="request"/>
+
+    <bean name="AccountService-prototype-1" class="org.glassfish.jersey.server.spring.test.AccountServiceImpl" scope="prototype">
+        <property name="defaultAccountBalance" value="987.65"/>
+    </bean>
+
+    <bean name="AccountService-prototype-2" class="org.glassfish.jersey.server.spring.test.AccountServiceImpl" scope="prototype"/>
+
+    <!-- Spring managed JAX-RS resources -->
+    <bean class="org.glassfish.jersey.server.spring.test.AccountSpringResource"/>
+
+    <bean class="org.glassfish.jersey.server.spring.test.ServiceResource"/>
+
+    <bean class="org.glassfish.jersey.server.spring.test.ControllerResource"/>
+
+    <bean class="org.glassfish.jersey.server.spring.test.RepositoryResource"/>
+
+    <bean class="org.glassfish.jersey.server.spring.test.EndpointResource"/>
+
+</beans>
diff --git a/tests/integration/spring4/src/main/webapp/WEB-INF/web.xml b/tests/integration/spring4/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..c3cff9d
--- /dev/null
+++ b/tests/integration/spring4/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
+
+  <module-name>jersey-spring-test</module-name>
+
+    <!-- use this to explicitly configure Spring -->
+    <!--
+    <listener>
+        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+    </listener>
+    <listener>
+        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
+    </listener>
+    <context-param>
+        <param-name>contextConfigLocation</param-name>
+        <param-value>classpath:/applicationContext.xml</param-value>
+    </context-param>
+    -->
+
+    <servlet>
+        <servlet-name>org.glassfish.jersey.server.spring.test.MyApplication</servlet-name>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>org.glassfish.jersey.server.spring.test.MyApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+
+</web-app>
+
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/AccountResourceITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/AccountResourceITCase.java
new file mode 100644
index 0000000..c9f9f17
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/AccountResourceITCase.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class AccountResourceITCase extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new Application();
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        final String r = target().path("/jersey/account/message").request().get(String.class);
+        assertEquals(r, "n/a");
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/AccountResourceTestBase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/AccountResourceTestBase.java
new file mode 100644
index 0000000..5c7774b
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/AccountResourceTestBase.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import java.math.BigDecimal;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Base class for JAX-RS resource tests.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public abstract class AccountResourceTestBase extends ResourceTestBase {
+
+    // test singleton scoped Spring bean injection using @Inject + @Autowired
+    @Test
+    public void testSingletonScopedSpringService() {
+        final BigDecimal newBalance = new BigDecimal(Math.random());
+        final WebTarget t = target(getResourceFullPath());
+
+        t.path("/singleton/xyz123").request().put(Entity.entity(newBalance.toString(), MediaType.TEXT_PLAIN_TYPE));
+        final BigDecimal balance = t.path("/singleton/autowired/xyz123").request().get(BigDecimal.class);
+        assertEquals(newBalance, balance);
+    }
+
+    @Test
+    public void testRequestScopedSpringService() {
+        final BigDecimal newBalance = new BigDecimal(Math.random());
+        final WebTarget t = target(getResourceFullPath());
+        final BigDecimal balance = t.path("request/abc456").request().put(Entity.text(newBalance), BigDecimal.class);
+        assertEquals(newBalance, balance);
+    }
+
+    @Test
+    public void testPrototypeScopedSpringService() {
+        final BigDecimal newBalance = new BigDecimal(Math.random());
+        final WebTarget t = target(getResourceFullPath());
+        final BigDecimal balance = t.path("prototype/abc456").request().put(Entity.text(newBalance), BigDecimal.class);
+        assertEquals(new BigDecimal("987.65"), balance);
+    }
+
+    @Test
+    public void testServletInjection() {
+        final WebTarget t = target(getResourceFullPath());
+
+        String server = t.path("server").request().get(String.class);
+        assertThat(server, startsWith("PASSED: "));
+
+        server = t.path("singleton/server").request().get(String.class);
+        assertThat(server, startsWith("PASSED: "));
+
+        server = t.path("singleton/autowired/server").request().get(String.class);
+        assertThat(server, startsWith("PASSED: "));
+
+        server = t.path("request/server").request().get(String.class);
+        assertThat(server, startsWith("PASSED: "));
+
+        server = t.path("prototype/server").request().get(String.class);
+        assertThat(server, startsWith("PASSED: "));
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/JerseyManagedITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/JerseyManagedITCase.java
new file mode 100644
index 0000000..4815e0e
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/JerseyManagedITCase.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for Jersey managed JAX-RS resources.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class JerseyManagedITCase extends AccountResourceTestBase {
+
+    @Override
+    protected ResourceConfig configure(final ResourceConfig rc) {
+        return rc.register(AccountJerseyResource.class);
+    }
+
+    @Override
+    protected String getResourcePath() {
+        return "/jersey/account";
+    }
+
+    @Test
+    public void testResourceScope() {
+        final WebTarget t = target(getResourceFullPath());
+        final String message = "hello, world";
+        final String echo = t.path("message").request().put(Entity.text(message), String.class);
+        assertEquals(message, echo);
+        final String msg = t.path("message").request().get(String.class);
+        assertEquals("n/a", msg);
+    }
+
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/ResourceTestBase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/ResourceTestBase.java
new file mode 100644
index 0000000..c460d7e
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/ResourceTestBase.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spring.SpringLifecycleListener;
+import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+/**
+ * Base class for JAX-RS resource tests.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public abstract class ResourceTestBase extends JerseyTest {
+
+    private static final String TEST_WEBAPP_CONTEXT_PATH = "jersey.spring.test.contextPath";
+    private static final String TEST_CONTAINER_FACTORY_EXTERNAL = "org.glassfish.jersey.test.external"
+            + ".ExternalTestContainerFactory";
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    @Override
+    protected Application configure() {
+        final ResourceConfig rc = new ResourceConfig()
+                .register(SpringLifecycleListener.class)
+                .register(RequestContextFilter.class);
+        TestUtil.registerHK2Services(rc);
+        rc.property("contextConfigLocation", "classpath:applicationContext.xml");
+        return configure(rc);
+    }
+
+    protected abstract ResourceConfig configure(ResourceConfig rc);
+
+    protected abstract String getResourcePath();
+
+    protected String getResourceFullPath() {
+        final String containerFactory = System.getProperty(TestProperties.CONTAINER_FACTORY);
+        if (TEST_CONTAINER_FACTORY_EXTERNAL.equals(containerFactory)) {
+            return System.getProperty(TEST_WEBAPP_CONTEXT_PATH) + getResourcePath();
+        }
+        return getResourcePath();
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedControllerITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedControllerITCase.java
new file mode 100644
index 0000000..cbe42c6
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedControllerITCase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for Spring managed JAX-RS resources with @Controller archetype.
+ *
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+public class SpringManagedControllerITCase extends ResourceTestBase {
+
+    @Override
+    protected ResourceConfig configure(final ResourceConfig rc) {
+        return rc.register(ControllerResource.class);
+    }
+
+    @Override
+    protected String getResourcePath() {
+        return "/spring/controller";
+    }
+
+    @Test
+    public void testResourceScope() {
+        final WebTarget t = target(getResourceFullPath());
+        final String message = "hello, world";
+        final String echo = t.path("message").request().put(Entity.text(message), String.class);
+        assertEquals(message, echo);
+        final String msg = t.path("message").request().get(String.class);
+        assertEquals(message, msg);
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedEndpointITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedEndpointITCase.java
new file mode 100644
index 0000000..fe96090
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedEndpointITCase.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for Spring managed JAX-RS resources with custom composite
+ * annotation that derives from @Component.
+ *
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+public class SpringManagedEndpointITCase extends ResourceTestBase {
+
+    @Override
+    protected ResourceConfig configure(final ResourceConfig rc) {
+        return rc.register(EndpointResource.class);
+    }
+
+    @Override
+    protected String getResourcePath() {
+        return "/spring/endpoint";
+    }
+
+    @Test
+    public void testResourceScope() {
+        final WebTarget t = target(getResourceFullPath());
+        final String message = "hello, world";
+        final String echo = t.path("message").request().put(Entity.text(message), String.class);
+        assertEquals(message, echo);
+        final String msg = t.path("message").request().get(String.class);
+        assertEquals(message, msg);
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedITCase.java
new file mode 100644
index 0000000..5eea429
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedITCase.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for Spring managed JAX-RS resources.
+ *
+ * @author Marko Asplund (marko.asplund at yahoo.com)
+ */
+public class SpringManagedITCase extends AccountResourceTestBase {
+
+    @Override
+    protected ResourceConfig configure(final ResourceConfig rc) {
+        return rc.register(AccountSpringResource.class);
+    }
+
+    @Override
+    protected String getResourcePath() {
+        return "/spring/account";
+    }
+
+    @Test
+    public void testResourceScope() {
+        final WebTarget t = target(getResourceFullPath());
+        final String message = "hello, world";
+        final String echo = t.path("message").request().put(Entity.text(message), String.class);
+        assertEquals(message, echo);
+        final String msg = t.path("message").request().get(String.class);
+        assertEquals(message, msg);
+    }
+
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedRepositoryITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedRepositoryITCase.java
new file mode 100644
index 0000000..5302a9e
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedRepositoryITCase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for Spring managed JAX-RS resources with @Repository archetype.
+ *
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+public class SpringManagedRepositoryITCase extends ResourceTestBase {
+
+    @Override
+    protected ResourceConfig configure(final ResourceConfig rc) {
+        return rc.register(RepositoryResource.class);
+    }
+
+    @Override
+    protected String getResourcePath() {
+        return "/spring/repository";
+    }
+
+    @Test
+    public void testResourceScope() {
+        final WebTarget t = target(getResourceFullPath());
+        final String message = "hello, world";
+        final String echo = t.path("message").request().put(Entity.text(message), String.class);
+        assertEquals(message, echo);
+        final String msg = t.path("message").request().get(String.class);
+        assertEquals(message, msg);
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedServiceITCase.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedServiceITCase.java
new file mode 100644
index 0000000..633ceea
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/SpringManagedServiceITCase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for Spring managed JAX-RS resources with @Service archetype.
+ *
+ * @author Konrad Garus (konrad.garus at gmail.com)
+ */
+public class SpringManagedServiceITCase extends ResourceTestBase {
+
+    @Override
+    protected ResourceConfig configure(final ResourceConfig rc) {
+        return rc.register(ServiceResource.class);
+    }
+
+    @Override
+    protected String getResourcePath() {
+        return "/spring/service";
+    }
+
+    @Test
+    public void testResourceScope() {
+        final WebTarget t = target(getResourceFullPath());
+        final String message = "hello, world";
+        final String echo = t.path("message").request().put(Entity.text(message), String.class);
+        assertEquals(message, echo);
+        final String msg = t.path("message").request().get(String.class);
+        assertEquals(message, msg);
+    }
+}
diff --git a/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/TestUtil.java b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/TestUtil.java
new file mode 100644
index 0000000..e4b4143
--- /dev/null
+++ b/tests/integration/spring4/src/test/java/org/glassfish/jersey/server/spring/test/TestUtil.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.server.spring.test;
+
+import javax.inject.Singleton;
+
+import org.glassfish.jersey.internal.inject.PerLookup;
+import org.glassfish.jersey.process.internal.RequestScoped;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.hk2.utilities.BuilderHelper;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+
+class TestUtil {
+
+    public static ResourceConfig registerHK2Services(final ResourceConfig rc) {
+        rc
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(BuilderHelper.link(HK2ServiceSingleton.class).in(Singleton.class).build());
+                    }
+                })
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(BuilderHelper.link(HK2ServiceRequestScoped.class).in(RequestScoped.class).build());
+                    }
+                })
+                .register(new AbstractBinder() {
+                    @Override
+                    protected void configure() {
+                        bind(BuilderHelper.link(HK2ServicePerLookup.class).in(PerLookup.class).build());
+                    }
+                });
+        return rc;
+    }
+}
diff --git a/tests/integration/tracing-support/pom.xml b/tests/integration/tracing-support/pom.xml
new file mode 100644
index 0000000..82e08eb
--- /dev/null
+++ b/tests/integration/tracing-support/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>tracing-support</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-integration-tracing-support</name>
+
+    <description>Tracing support integration test</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <configuration>
+                    <systemPropertyVariables>
+                        <!-- client side config works - failsafe plugin forks jvm -->
+                        <java.util.logging.config.file>${project.build.testOutputDirectory}/logging.properties</java.util.logging.config.file>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+                <configuration>
+                    <connectors>
+                        <connector>
+                            <responseHeaderSize>16192</responseHeaderSize>
+                        </connector>
+                    </connectors>
+                    <!-- server side config does not fork with jetty:run goal - it uses same jvm
+                         use maven '-D' option instead:
+                             mvn _goal_ -Djava.util.logging.config.file=target/test-classes/logging.properties
+                    <systemProperties>
+                        <systemProperty>
+                            <name>java.util.logging.config.file</name>
+                            <value>${project.build.testOutputDirectory}/logging.properties</value>
+                        </systemProperty>
+                    </systemProperties>
+                    -->
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AllTracingSupport.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AllTracingSupport.java
new file mode 100644
index 0000000..f260a39
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AllTracingSupport.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.TracingConfig;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/ALL")
+public class AllTracingSupport extends TracingSupport {
+
+    public AllTracingSupport() {
+        super(TracingConfig.ALL);
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AsyncResource.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AsyncResource.java
new file mode 100644
index 0000000..338172c
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AsyncResource.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.tracing;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+/**
+* @author Libor Kramolis (libor.kramolis at oracle.com)
+*/
+@Path("/async")
+public class AsyncResource {
+
+    @Path("{name}")
+    @GET
+    public void get(@PathParam("name") String name, @Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new Message(new StringBuffer(name).reverse().toString()));
+    }
+
+    @POST
+    public void post(Message post, @Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new Message(new StringBuffer(post.getText()).reverse().toString()));
+    }
+
+    @Path("sub-resource-method")
+    @POST
+    public void postSub(Message post, @Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new Message(new StringBuffer(post.getText()).reverse().toString()));
+    }
+
+    @Path("sub-resource-locator")
+    public AsyncSubResource getSubLoc() {
+        return new AsyncSubResource();
+    }
+
+    @Path("sub-resource-locator-null")
+    public AsyncSubResource getSubLocNull() {
+        return null;
+    }
+
+    @GET
+    @Path("runtime-exception")
+    public void getRuntimeException(@Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new RuntimeException("Something does not work ..."));
+    }
+
+    @GET
+    @Path("mapped-exception")
+    public void getMappedException(@Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new TestException("This could be client fault ..."));
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AsyncSubResource.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AsyncSubResource.java
new file mode 100644
index 0000000..9666448
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/AsyncSubResource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 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.integration.tracing;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+
+/**
+* @author Libor Kramolis (libor.kramolis at oracle.com)
+*/
+@Path("/")
+public class AsyncSubResource {
+    @POST
+    public void post(Message post, @Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new Message(new StringBuffer(post.getText()).reverse().toString()));
+    }
+
+    @Path("sub-resource-method")
+    @POST
+    public void postSub(Message post, @Suspended final AsyncResponse asyncResponse) {
+        asyncResponse.resume(new Message(new StringBuffer(post.getText()).reverse().toString()));
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerRequestFilter68.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerRequestFilter68.java
new file mode 100644
index 0000000..bbf688a
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerRequestFilter68.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Priority(68)
+public class ContainerRequestFilter68 implements ContainerRequestFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        //System.out.println("*** ContainerRequestFilter68.filter");
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerRequestFilterNoPriority.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerRequestFilterNoPriority.java
new file mode 100644
index 0000000..80c095c
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerRequestFilterNoPriority.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+public class ContainerRequestFilterNoPriority implements ContainerRequestFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        //System.out.println("*** ContainerRequestFilterNoPriority.filter");
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerResponseFilter5001.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerResponseFilter5001.java
new file mode 100644
index 0000000..f674354
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerResponseFilter5001.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Priority(5001)
+public class ContainerResponseFilter5001 implements ContainerResponseFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        //System.out.println("*** ContainerResponseFilter5001.filter");
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerResponseFilterNoPriority.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerResponseFilterNoPriority.java
new file mode 100644
index 0000000..381da17
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ContainerResponseFilterNoPriority.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+public class ContainerResponseFilterNoPriority implements ContainerResponseFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        //System.out.println("*** ContainerResponseFilterNoPriority.filter");
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Message.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Message.java
new file mode 100644
index 0000000..b8f4f6a
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Message.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Message {
+
+    private String text;
+
+    public Message() {
+    }
+
+    public Message(String text) {
+        //System.out.println("*** Message: |" + text + "|");
+        this.text = text;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Message message = (Message) o;
+
+        if (text != null ? !text.equals(message.text) : message.text != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return text != null ? text.hashCode() : 0;
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyReaderGeneric.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyReaderGeneric.java
new file mode 100644
index 0000000..4d93336
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyReaderGeneric.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Consumes("*/*")
+public class MessageBodyReaderGeneric implements MessageBodyReader<Message> {
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return false;
+    }
+
+    @Override
+    public Message readFrom(Class<Message> type,
+                            Type genericType,
+                            Annotation[] annotations,
+                            MediaType mediaType,
+                            MultivaluedMap<String, String> httpHeaders,
+                            InputStream entityStream) throws IOException, WebApplicationException {
+        return null;
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyReaderTestFormat.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyReaderTestFormat.java
new file mode 100644
index 0000000..81d0941
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyReaderTestFormat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+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 org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Consumes(Utils.APPLICATION_X_JERSEY_TEST)
+public class MessageBodyReaderTestFormat implements MessageBodyReader<Message> {
+
+    boolean serverSide = true;
+
+    public MessageBodyReaderTestFormat() {
+    }
+
+    public MessageBodyReaderTestFormat(final boolean serverSide) {
+        this.serverSide = serverSide;
+    }
+
+    @Override
+    public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                              final MediaType mediaType) {
+        return type.isAssignableFrom(Message.class);
+    }
+
+    @Override
+    public Message readFrom(final Class<Message> type, final Type genericType, final Annotation[] annotations,
+                            final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders,
+                            final InputStream entityStream) throws IOException, WebApplicationException {
+        final BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream, MessageUtils.getCharset(mediaType)));
+
+        final String line = reader.readLine();
+        if (line == null || !line.startsWith(Utils.FORMAT_PREFIX) || !line.endsWith(Utils.FORMAT_SUFFIX)) {
+            throw new WebApplicationException(
+                    new IllegalArgumentException("Input content '" + line + "' is not in a valid format!"));
+        }
+        final String text = line.substring(Utils.FORMAT_PREFIX.length(), line.length() - Utils.FORMAT_SUFFIX.length());
+
+        if (serverSide) {
+            Utils.throwException(text, this,
+                    Utils.TestAction.MESSAGE_BODY_READER_THROW_WEB_APPLICATION,
+                    Utils.TestAction.MESSAGE_BODY_READER_THROW_PROCESSING,
+                    Utils.TestAction.MESSAGE_BODY_READER_THROW_ANY);
+        }
+
+        return new Message(text);
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyWriterGeneric.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyWriterGeneric.java
new file mode 100644
index 0000000..4e295ba
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyWriterGeneric.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Produces("*/*")
+public class MessageBodyWriterGeneric implements MessageBodyWriter<Message> {
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return false;
+    }
+
+    @Override
+    public long getSize(Message message, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(Message message,
+                        Class<?> type,
+                        Type genericType,
+                        Annotation[] annotations,
+                        MediaType mediaType,
+                        MultivaluedMap<String, Object> httpHeaders,
+                        OutputStream entityStream) throws IOException, WebApplicationException {
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyWriterTestFormat.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyWriterTestFormat.java
new file mode 100644
index 0000000..6983d18
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/MessageBodyWriterTestFormat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+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 org.glassfish.jersey.message.MessageUtils;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Produces(Utils.APPLICATION_X_JERSEY_TEST)
+public class MessageBodyWriterTestFormat implements MessageBodyWriter<Message> {
+
+    boolean serverSide = true;
+
+    public MessageBodyWriterTestFormat() {
+    }
+
+    public MessageBodyWriterTestFormat(final boolean serverSide) {
+        this.serverSide = serverSide;
+    }
+
+    @Override
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.isAssignableFrom(Message.class);
+    }
+
+    @Override
+    public long getSize(final Message message, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final Message message, final Class<?> type, final Type genericType, final Annotation[] annotations,
+                        final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        if (serverSide) {
+            Utils.throwException(message.getText(), this,
+                    Utils.TestAction.MESSAGE_BODY_WRITER_THROW_WEB_APPLICATION,
+                    Utils.TestAction.MESSAGE_BODY_WRITER_THROW_PROCESSING,
+                    Utils.TestAction.MESSAGE_BODY_WRITER_THROW_ANY);
+        }
+
+        final OutputStreamWriter writer = new OutputStreamWriter(entityStream, MessageUtils.getCharset(mediaType));
+        writer.write(Utils.FORMAT_PREFIX);
+        writer.write(message.getText());
+        writer.write(Utils.FORMAT_SUFFIX);
+        writer.flush();
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/OnDemandTracingSupport.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/OnDemandTracingSupport.java
new file mode 100644
index 0000000..0a15268
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/OnDemandTracingSupport.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.ApplicationPath;
+
+import org.glassfish.jersey.server.TracingConfig;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@ApplicationPath("/ON_DEMAND")
+public class OnDemandTracingSupport extends TracingSupport {
+
+    public OnDemandTracingSupport() {
+        super(TracingConfig.ON_DEMAND);
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/PreMatchingContainerRequestFilter23.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/PreMatchingContainerRequestFilter23.java
new file mode 100644
index 0000000..6cc32a9
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/PreMatchingContainerRequestFilter23.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@PreMatching
+@Priority(23)
+public class PreMatchingContainerRequestFilter23 implements ContainerRequestFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        //System.out.println("*** PreMatchingContainerRequestFilter23.filter");
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/PreMatchingContainerRequestFilter42.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/PreMatchingContainerRequestFilter42.java
new file mode 100644
index 0000000..fa83983
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/PreMatchingContainerRequestFilter42.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@PreMatching
+@Priority(42)
+public class PreMatchingContainerRequestFilter42 implements ContainerRequestFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        //System.out.println("*** PreMatchingContainerRequestFilter42.filter");
+        Utils.throwException(requestContext, this,
+                Utils.TestAction.PRE_MATCHING_REQUEST_FILTER_THROW_WEB_APPLICATION,
+                Utils.TestAction.PRE_MATCHING_REQUEST_FILTER_THROW_PROCESSING,
+                Utils.TestAction.PRE_MATCHING_REQUEST_FILTER_THROW_ANY);
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ReaderInterceptor14.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ReaderInterceptor14.java
new file mode 100644
index 0000000..e37971c
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ReaderInterceptor14.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.annotation.Priority;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Priority(14)
+public class ReaderInterceptor14 implements ReaderInterceptor {
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+        //System.out.println("*** ReaderInterceptor14.aroundReadFrom: BEFORE");
+        try {
+            Thread.sleep(42);
+        } catch (InterruptedException e) {
+        }
+        try {
+            return context.proceed();
+        } finally {
+            try {
+                Thread.sleep(42);
+            } catch (InterruptedException e) {
+            }
+            //System.out.println("*** ReaderInterceptor14.aroundReadFrom: AFTER");
+        }
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ReaderInterceptor18.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ReaderInterceptor18.java
new file mode 100644
index 0000000..3d3f444
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/ReaderInterceptor18.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import javax.annotation.Priority;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Priority(18)
+public class ReaderInterceptor18 implements ReaderInterceptor {
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+        //System.out.println("*** ReaderInterceptor18.aroundReadFrom: BEFORE");
+        try {
+            return context.proceed();
+        } finally {
+            //System.out.println("*** ReaderInterceptor18.aroundReadFrom: AFTER");
+        }
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Resource.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Resource.java
new file mode 100644
index 0000000..c13b57d
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Resource.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+/**
+* @author Libor Kramolis (libor.kramolis at oracle.com)
+*/
+@Path("/root")
+public class Resource {
+
+    @Path("{name}")
+    @GET
+    public Message get(@PathParam("name") String name) {
+        return new Message(new StringBuffer(name).reverse().toString());
+    }
+
+    @POST
+    public Message post(Message post) {
+        return new Message(new StringBuffer(post.getText()).reverse().toString());
+    }
+
+    @Path("sub-resource-method")
+    @POST
+    public Message postSub(Message post) {
+        return new Message(new StringBuffer(post.getText()).reverse().toString());
+    }
+
+    @Path("sub-resource-locator")
+    public SubResource getSubLoc() {
+        return new SubResource();
+    }
+
+    @Path("sub-resource-locator-null")
+    public SubResource getSubLocNull() {
+        return null;
+    }
+
+    @GET
+    @Path("runtime-exception")
+    public Message getRuntimeException() {
+        throw new RuntimeException("Something does not work ...");
+    }
+
+    @GET
+    @Path("mapped-exception")
+    public Message getMappedException() {
+        throw new TestException("This could be client fault ...");
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/SubResource.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/SubResource.java
new file mode 100644
index 0000000..b803c3e
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/SubResource.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+/**
+* @author Libor Kramolis (libor.kramolis at oracle.com)
+*/
+@Path("/")
+public class SubResource {
+    @POST
+    public Message post(Message post) {
+        return new Message(new StringBuffer(post.getText()).reverse().toString());
+    }
+
+    @Path("sub-resource-method")
+    @POST
+    public Message postSub(Message post) {
+        return new Message(new StringBuffer(post.getText()).reverse().toString());
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestException.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestException.java
new file mode 100644
index 0000000..26dccfd
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TestException extends RuntimeException {
+
+    public TestException() {
+        super();
+    }
+
+    public TestException(String message) {
+        super(message);
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExceptionMapper.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExceptionMapper.java
new file mode 100644
index 0000000..04d6dec
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExceptionMapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Priority(Priorities.USER)
+@Provider
+public class TestExceptionMapper implements ExceptionMapper<TestException> {
+
+    @Override
+    public Response toResponse(TestException throwable) {
+        return Response.status(501).build();
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExtendedExceptionMapperGeneric.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExtendedExceptionMapperGeneric.java
new file mode 100644
index 0000000..6a0cc9d
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExtendedExceptionMapperGeneric.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.Priorities;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.spi.ExtendedExceptionMapper;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Priority(Priorities.USER + 1)
+@Provider
+public class TestExtendedExceptionMapperGeneric implements ExtendedExceptionMapper<Exception> {
+
+    @Override
+    public Response toResponse(Exception throwable) {
+        return Response.status(500).build();
+    }
+
+    @Override
+    public boolean isMappable(Exception throwable) {
+        return !(throwable instanceof RuntimeException);
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExtendedExceptionMapperRuntime.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExtendedExceptionMapperRuntime.java
new file mode 100644
index 0000000..0161b37
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TestExtendedExceptionMapperRuntime.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.Priorities;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import javax.annotation.Priority;
+
+import org.glassfish.jersey.spi.ExtendedExceptionMapper;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Priority(Priorities.USER + 2)
+@Provider
+public class TestExtendedExceptionMapperRuntime implements ExtendedExceptionMapper<Throwable> {
+
+    @Override
+    public Response toResponse(Throwable throwable) {
+        return Response.status(500).build();
+    }
+
+    @Override
+    public boolean isMappable(Throwable throwable) {
+        return (throwable instanceof RuntimeException) && !(throwable instanceof WebApplicationException);
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TracingSupport.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TracingSupport.java
new file mode 100644
index 0000000..fabb7a2
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/TracingSupport.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.TracingConfig;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class TracingSupport extends ResourceConfig {
+
+    public TracingSupport(TracingConfig type) {
+        Utils.configure(this);
+        property(ServerProperties.TRACING, type.name());
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Utils.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Utils.java
new file mode 100644
index 0000000..aa9b45d
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/Utils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Configurable;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public final class Utils {
+
+    public static final String HEADER_TRACING_PREFIX = "X-Jersey-Tracing-";
+
+    public static final String APPLICATION_X_JERSEY_TEST = "application/x-jersey-test";
+    public static final String FORMAT_PREFIX = "-=#[";
+    public static final String FORMAT_SUFFIX = "]#=-";
+    public static final String HEADER_TEST_ACTION = "test-action";
+
+    private Utils() {
+    }
+
+    public static void configure(ResourceConfig configurable) {
+        configurable.packages(Utils.class.getPackage().getName());
+//        OR:
+//        configure((Configurable)configurable);
+//        configurable.register(PreMatchingContainerRequestFilter23.class);
+//        configurable.register(PreMatchingContainerRequestFilter42.class);
+//        configurable.register(ContainerRequestFilter68.class);
+//        configurable.register(ContainerRequestFilterNoPriority.class);
+//        configurable.register(ContainerResponseFilter5001.class);
+//        configurable.register(ContainerResponseFilterNoPriority.class);
+//        configurable.register(TestExceptionMapper.class);
+//        configurable.register(TestExtendedExceptionMapperGeneric.class);
+//        configurable.register(TestExtendedExceptionMapperRuntime.class);
+//        configurable.register(Resource.class);
+//        configurable.register(SubResource.class);
+        configurable.register(new LoggingFeature(Logger.getAnonymousLogger(), LoggingFeature.Verbosity.PAYLOAD_ANY));
+    }
+
+    public static void configure(ClientConfig configurable) {
+        configure((Configurable) configurable);
+    }
+
+    public static void configure(Configurable configurable) {
+        configurable.register(ReaderInterceptor14.class);
+        configurable.register(ReaderInterceptor18.class);
+        configurable.register(WriterInterceptor39.class);
+        configurable.register(WriterInterceptor45.class);
+        configurable.register(new MessageBodyReaderTestFormat(false));
+        configurable.register(MessageBodyReaderGeneric.class);
+        configurable.register(new MessageBodyWriterTestFormat(false));
+        configurable.register(MessageBodyWriterGeneric.class);
+    }
+
+    public static void throwException(final ContainerRequestContext requestContext,
+                                      final Object fromContext,
+                                      final TestAction throwWebApplicationException,
+                                      final TestAction throwProcessingException,
+                                      final TestAction throwAnyException) {
+        final Utils.TestAction testAction = Utils.getTestAction(requestContext);
+        throwExceptionImpl(testAction, fromContext, throwWebApplicationException, throwProcessingException, throwAnyException);
+    }
+
+    public static void throwException(final String testActionName,
+                                      final Object fromContext,
+                                      final TestAction throwWebApplicationException,
+                                      final TestAction throwProcessingException,
+                                      final TestAction throwAnyException) {
+        Utils.TestAction testAction;
+        try {
+            testAction = TestAction.valueOf(testActionName);
+        } catch (IllegalArgumentException ex) {
+            try {
+                testAction = TestAction.valueOf(new StringBuffer(testActionName).reverse().toString());
+            } catch (IllegalArgumentException ex2) {
+                testAction = null;
+            }
+        }
+        throwExceptionImpl(testAction, fromContext, throwWebApplicationException, throwProcessingException, throwAnyException);
+    }
+
+    private static void throwExceptionImpl(final Utils.TestAction testAction,
+                                           final Object fromContext,
+                                           final TestAction throwWebApplicationException,
+                                           final TestAction throwProcessingException,
+                                           final TestAction throwAnyException) {
+        final String message = "Test Exception from " + fromContext.getClass().getName();
+        if (testAction == null) {
+            // do nothing
+        } else if (testAction == throwWebApplicationException) {
+            throw new WebApplicationException(message);
+        } else if (testAction == throwProcessingException) {
+            throw new ProcessingException(message);
+        } else if (testAction == throwAnyException) {
+            throw new RuntimeException(message);
+        }
+    }
+
+    public static TestAction getTestAction(ContainerRequestContext requestContext) {
+        String testActionHeader = requestContext.getHeaderString(HEADER_TEST_ACTION);
+        TestAction testAction = null;
+        if (testActionHeader != null) {
+            testAction = TestAction.valueOf(testActionHeader);
+        }
+        return testAction;
+    }
+
+    public static enum TestAction {
+        PRE_MATCHING_REQUEST_FILTER_THROW_WEB_APPLICATION,
+        PRE_MATCHING_REQUEST_FILTER_THROW_PROCESSING,
+        PRE_MATCHING_REQUEST_FILTER_THROW_ANY,
+        MESSAGE_BODY_READER_THROW_WEB_APPLICATION,
+        MESSAGE_BODY_READER_THROW_PROCESSING,
+        MESSAGE_BODY_READER_THROW_ANY,
+        MESSAGE_BODY_WRITER_THROW_WEB_APPLICATION,
+        MESSAGE_BODY_WRITER_THROW_PROCESSING,
+        MESSAGE_BODY_WRITER_THROW_ANY;
+        //TODO add other *_THROW_* actions to throw exception from other stages
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/WriterInterceptor39.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/WriterInterceptor39.java
new file mode 100644
index 0000000..f6257db
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/WriterInterceptor39.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import java.util.Date;
+import javax.annotation.Priority;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Priority(39)
+public class WriterInterceptor39 implements WriterInterceptor {
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+        //System.out.println("*** WriterInterceptor39.aroundWriteTo: BEFORE");
+        try {
+            Thread.sleep(42);
+        } catch (InterruptedException e) {
+        }
+        context.getHeaders().putSingle(WriterInterceptor39.class.getSimpleName(), new Date());
+        context.proceed();
+        //System.out.println("*** WriterInterceptor39.aroundWriteTo: AFTER");
+    }
+}
diff --git a/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/WriterInterceptor45.java b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/WriterInterceptor45.java
new file mode 100644
index 0000000..a76f13f
--- /dev/null
+++ b/tests/integration/tracing-support/src/main/java/org/glassfish/jersey/tests/integration/tracing/WriterInterceptor45.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import java.util.Date;
+import javax.annotation.Priority;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Provider
+@Priority(45)
+public class WriterInterceptor45 implements WriterInterceptor {
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+        //System.out.println("*** WriterInterceptor45.aroundWriteTo: BEFORE");
+        context.getHeaders().putSingle(WriterInterceptor45.class.getSimpleName(), new Date());
+        context.proceed();
+        try {
+            Thread.sleep(42);
+        } catch (InterruptedException e) {
+        }
+        //System.out.println("*** WriterInterceptor45.aroundWriteTo: AFTER");
+    }
+}
diff --git a/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/AllTracingSupportITCase.java b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/AllTracingSupportITCase.java
new file mode 100644
index 0000000..430789e
--- /dev/null
+++ b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/AllTracingSupportITCase.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.TracingConfig;
+import org.glassfish.jersey.server.internal.ServerTraceEvent;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * 'ALL' tracing support test that is running in external Jetty container.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@RunWith(Parameterized.class)
+public class AllTracingSupportITCase extends JerseyTest {
+
+    private final String resourcePath;
+
+    public AllTracingSupportITCase(String resourcePath) {
+        this.resourcePath = resourcePath;
+    }
+
+    @Parameterized.Parameters(name = "{index}: {0}")
+    public static List<Object[]> testData() {
+        return Arrays.asList(new Object[][] {
+                {"/root"},
+                {"/async"},
+        });
+    }
+
+    //
+    // JerseyTest
+    //
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return new AllTracingSupport();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig clientConfig) {
+        Utils.configure(clientConfig);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    //
+    // tests
+    //
+
+    @Test
+    public void testGet() {
+        Invocation.Builder builder = resource(resourcePath).path("NAME").request();
+        Response response = builder.get();
+        assertXJerseyTrace(response, false);
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testRuntimeException() {
+        Invocation.Builder builder = resource(resourcePath).path("runtime-exception").request();
+
+        Response response = builder.get();
+        assertXJerseyTrace(response, true);
+        assertEquals(500, response.getStatus());
+    }
+
+    @Test
+    public void testMappedException() throws InterruptedException, IOException {
+        Invocation.Builder builder = resource(resourcePath).path("mapped-exception").request();
+
+        Response response = builder.get();
+        assertXJerseyTrace(response, true);
+        assertEquals(501, response.getStatus());
+    }
+
+    @Test
+    public void testGet405() {
+        Invocation.Builder builder = resource(resourcePath).request();
+
+        Response response = builder.get();
+        assertXJerseyTrace(response, false);
+        assertEquals(405, response.getStatus());
+    }
+
+    @Test
+    public void testPostSubResourceMethod() {
+        Invocation.Builder builder = resource(resourcePath).path("sub-resource-method").request();
+
+        Response response = builder.post(Entity.entity(new Message("POST"), Utils.APPLICATION_X_JERSEY_TEST));
+        assertXJerseyTrace(response, false);
+        assertEquals("TSOP", response.readEntity(Message.class).getText());
+    }
+
+    @Test
+    public void testPostSubResourceLocator() {
+        Invocation.Builder builder = resource(resourcePath).path("sub-resource-locator").request();
+
+        Response response = builder.post(Entity.entity(new Message("POST"), Utils.APPLICATION_X_JERSEY_TEST));
+        assertXJerseyTrace(response, false);
+        assertEquals("TSOP", response.readEntity(Message.class).getText());
+    }
+
+    @Test
+    public void testPostSubResourceLocatorNull() {
+        Invocation.Builder builder = resource(resourcePath).path("sub-resource-locator-null").request();
+
+        Response response = builder.post(Entity.entity(new Message("POST"), Utils.APPLICATION_X_JERSEY_TEST));
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    public void testPostSubResourceLocatorSubResourceMethod() {
+        Invocation.Builder builder = resource(resourcePath).path("sub-resource-locator").path("sub-resource-method").request();
+
+        Response response = builder.post(Entity.entity(new Message("POST"), Utils.APPLICATION_X_JERSEY_TEST));
+        assertXJerseyTrace(response, false);
+        assertEquals("TSOP", response.readEntity(Message.class).getText());
+    }
+
+    @Test
+    public void testTraceInnerException() {
+        // PRE_MATCHING_REQUEST_FILTER
+        testTraceInnerExceptionImpl(Utils.TestAction.PRE_MATCHING_REQUEST_FILTER_THROW_WEB_APPLICATION, 500, false);
+        testTraceInnerExceptionImpl(Utils.TestAction.PRE_MATCHING_REQUEST_FILTER_THROW_PROCESSING, 500, true);
+        testTraceInnerExceptionImpl(Utils.TestAction.PRE_MATCHING_REQUEST_FILTER_THROW_ANY, 500, true);
+        // MESSAGE_BODY_WRITER
+        testTraceInnerExceptionImpl(Utils.TestAction.MESSAGE_BODY_WRITER_THROW_WEB_APPLICATION, 500, false);
+        testTraceInnerExceptionImpl(Utils.TestAction.MESSAGE_BODY_WRITER_THROW_PROCESSING, 500, true);
+        testTraceInnerExceptionImpl(Utils.TestAction.MESSAGE_BODY_WRITER_THROW_ANY, 500, true);
+        // MESSAGE_BODY_READER
+        testTraceInnerExceptionImpl(Utils.TestAction.MESSAGE_BODY_READER_THROW_WEB_APPLICATION, 500, false);
+        testTraceInnerExceptionImpl(Utils.TestAction.MESSAGE_BODY_READER_THROW_PROCESSING, 500, true);
+        testTraceInnerExceptionImpl(Utils.TestAction.MESSAGE_BODY_READER_THROW_ANY, 500, true);
+    }
+
+    //
+    // utils
+    //
+
+    private void testTraceInnerExceptionImpl(Utils.TestAction testAction, int expectedStatus, boolean exceptionExpected) {
+        Invocation.Builder builder = resource(resourcePath).request();
+        builder.header(Utils.HEADER_TEST_ACTION, testAction);
+
+        Response response = builder.post(Entity.entity(new Message(testAction.name()), Utils.APPLICATION_X_JERSEY_TEST));
+        assertXJerseyTrace(response, exceptionExpected);
+        assertEquals(expectedStatus, response.getStatus());
+    }
+
+    private void assertXJerseyTrace(Response response, boolean exceptionExpected) {
+        int finished = 0;
+        int exceptionMapping = 0;
+
+        for (String k : response.getHeaders().keySet()) {
+            if (k.toLowerCase().startsWith(Utils.HEADER_TRACING_PREFIX.toLowerCase())) {
+                String value = response.getHeaderString(k);
+                if (value.startsWith(ServerTraceEvent.FINISHED.category())) {
+                    finished++;
+                } else if (value.startsWith(ServerTraceEvent.EXCEPTION_MAPPING.category())) {
+                    exceptionMapping++;
+                }
+            }
+        }
+        assertEquals("Just one FINISHED expected!", 1, finished);
+        if (exceptionExpected) {
+            assertEquals("EXCEPTION expected!", 1, exceptionMapping);
+        } else {
+            assertEquals("EXCEPTION NOT expected!", 0, exceptionMapping);
+        }
+    }
+
+    private WebTarget resource(String path) {
+        return target("/" + TracingConfig.ALL).path(path);
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/OnDemandTracingSupportITCase.java b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/OnDemandTracingSupportITCase.java
new file mode 100644
index 0000000..05ecceb
--- /dev/null
+++ b/tests/integration/tracing-support/src/test/java/org/glassfish/jersey/tests/integration/tracing/OnDemandTracingSupportITCase.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2013, 2018 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.integration.tracing;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.internal.TracingLogger;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.TracingConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.external.ExternalTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * 'ON_DEMAND' tracing support test that is running in external Jetty container.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class OnDemandTracingSupportITCase extends JerseyTest {
+
+    //
+    // JerseyTest
+    //
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+        enable(TestProperties.DUMP_ENTITY);
+
+        return new OnDemandTracingSupport();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig clientConfig) {
+        Utils.configure(clientConfig);
+    }
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return new ExternalTestContainerFactory();
+    }
+
+    //
+    // tests
+    //
+
+    @Test
+    public void testNoTracing() {
+        Invocation.Builder builder = resource("/root").request();
+        Response response = builder.post(Entity.entity(new Message("POST"), Utils.APPLICATION_X_JERSEY_TEST));
+        assertFalse(hasX_Jersey_Trace(response));
+        assertEquals("TSOP", response.readEntity(Message.class).getText());
+    }
+
+    @Test
+    public void testTraceAcceptEmpty() {
+        testTraceAccept("");
+    }
+
+    @Test
+    public void testTraceAcceptTrue() {
+        testTraceAccept("true");
+    }
+
+    @Test
+    public void testTraceAcceptWhatever() {
+        testTraceAccept("whatever");
+    }
+
+    private void testTraceAccept(String acceptValue) {
+        Invocation.Builder builder = resource("/root").request();
+        Response response = builder.header(TracingLogger.HEADER_ACCEPT, acceptValue)
+                .post(Entity.entity(new Message("POST"), Utils.APPLICATION_X_JERSEY_TEST));
+        assertTrue(hasX_Jersey_Trace(response));
+        assertEquals("TSOP", response.readEntity(Message.class).getText());
+    }
+
+    //
+    // utils
+    //
+
+    private boolean hasX_Jersey_Trace(Response response) {
+        for (String k : response.getHeaders().keySet()) {
+            if (k.toLowerCase().startsWith(Utils.HEADER_TRACING_PREFIX.toLowerCase())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private WebTarget resource(String path) {
+        return target("/" + TracingConfig.ON_DEMAND).path(path);
+    }
+
+}
diff --git a/tests/integration/tracing-support/src/test/resources/logging.properties b/tests/integration/tracing-support/src/test/resources/logging.properties
new file mode 100644
index 0000000..6ba7395
--- /dev/null
+++ b/tests/integration/tracing-support/src/test/resources/logging.properties
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2013, 2018 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
+#
+
+#All attributes details
+handlers=java.util.logging.ConsoleHandler
+java.util.logging.ConsoleHandler.level=FINEST
+java.util.logging.SimpleFormatter.format=%4$-7s [%3$s] %5$s%6$s%n
+
+#All log level details
+.level=INFO
+org.glassfish.jersey.level=CONFIG
+org.glassfish.jersey.tracing.level=FINEST
diff --git a/tests/mem-leaks/pom.xml b/tests/mem-leaks/pom.xml
new file mode 100644
index 0000000..7a6b4c1
--- /dev/null
+++ b/tests/mem-leaks/pom.xml
@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.memleaks</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-memleak</name>
+
+    <description>
+        Memory leaks test maven parent module.
+
+        This module aggregates maven based configuration for all memory leak related modules.
+
+        Note that the majority of plugin execution in maven sub-modules is bound to phase none by default. The reason is that
+        memory leak tests are executed only if one of memory leak maven profiles are activated.
+    </description>
+
+    <properties>
+
+        <memleak.jersey.version>2.28-SNAPSHOT</memleak.jersey.version>
+
+        <memleak.tmpdir>/tmp</memleak.tmpdir>
+
+        <memleak.jvm.maxheap>512m</memleak.jvm.maxheap>
+        <memleak.jvm.heapdumpdir>${project.build.directory}</memleak.jvm.heapdumpdir>
+
+        <external.container.factory>org.glassfish.jersey.test.external.ExternalTestContainerFactory</external.container.factory>
+        <external.container.port>${external.container.port.dynamic}</external.container.port>
+        <external.container.contextRoot>${project.artifactId}-${project.version}</external.container.contextRoot>
+        <external.container.warPath>${project.build.directory}/${project.build.finalName}.${project.packaging}
+        </external.container.warPath>
+        <external.container.logFile>${project.build.directory}/container.log</external.container.logFile>
+
+        <maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format>
+        <memleak.timestamp>${maven.build.timestamp}</memleak.timestamp>
+
+        <memleak.glassfish.zip>${memleak.tmpdir}/memleak-test-glassfish-${memleak.timestamp}.zip</memleak.glassfish.zip>
+        <memleak.glassfish.adminPort>${memleak.glassfish.adminPort.dynamic}</memleak.glassfish.adminPort>
+        <memleak.glassfish.url>${jersey.runner.distUrl}</memleak.glassfish.url>
+        <memleak.glassfish.maxheap>${memleak.jvm.maxheap}</memleak.glassfish.maxheap>
+
+        <memleak.wls.jar>${memleak.tmpdir}/memleak-test-wls-${memleak.timestamp}.jar</memleak.wls.jar>
+        <memleak.wls.url>${jersey.runner.distUrl}</memleak.wls.url>
+        <memleak.wls.maxheap>${memleak.jvm.maxheap}</memleak.wls.maxheap>
+
+        <memleak.tomcat.version>8.0.23</memleak.tomcat.version>
+        <memleak.tomcat.url>
+            http://archive.apache.org/dist/tomcat/tomcat-8/v${memleak.tomcat.version}/bin/apache-tomcat-${memleak.tomcat.version}.zip
+        </memleak.tomcat.url>
+        <memleak.tomcat.zip>${memleak.tmpdir}/memleak-test-tomcat-${memleak.tomcat.version}-${memleak.timestamp}.zip
+        </memleak.tomcat.zip>
+        <memleak.tomcat.maxheap>${memleak.jvm.maxheap}</memleak.tomcat.maxheap>
+
+        <!-- by default, all common executions are bound to the pahse 'none' so that they're not executed -->
+        <phase.common.pre-integration-test>none</phase.common.pre-integration-test>
+        <phase.common.post-integration-test>none</phase.common.post-integration-test>
+
+        <!-- by default, redeployment executions are bound to the phase 'none' so that they're not executed -->
+        <phase.redeployment.pre-integration-test>none</phase.redeployment.pre-integration-test>
+        <phase.redeployment.integration-test>none</phase.redeployment.integration-test>
+        <phase.redeployment.post-integration-test>none</phase.redeployment.post-integration-test>
+
+        <!-- by default, test-cases executions are bound to the phase 'none' so that they're not executed -->
+        <phase.tests.test>none</phase.tests.test>
+        <phase.tests.pre-integration-test>none</phase.tests.pre-integration-test>
+        <phase.tests.integration-test>none</phase.tests.integration-test>
+        <phase.tests.post-integration-test>none</phase.tests.post-integration-test>
+        <phase.tests.verify>none</phase.tests.verify>
+    </properties>
+
+    <modules>
+        <module>redeployment</module>
+        <module>test-cases</module>
+    </modules>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>reserve-network-ports</id>
+                            <goals>
+                                <goal>reserve-network-port</goal>
+                            </goals>
+                            <phase>${phase.common.pre-integration-test}</phase>
+                            <configuration>
+                                <portNames>
+                                    <portName>external.container.port.dynamic</portName>
+                                </portNames>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                    <artifactId>container-runner-maven-plugin</artifactId>
+                    <version>${project.version}</version>
+                    <configuration>
+                        <warPath>${external.container.warPath}</warPath>
+                        <contextRoot>${external.container.contextRoot}</contextRoot>
+                        <port>${external.container.port}</port>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+    </build>
+
+    <profiles>
+        <profile>
+            <id>gf4</id>
+            <properties>
+                <external.container.logFile>
+                    ${project.build.directory}/glassfish4/glassfish/domains/memleak_test_domain/logs/server.log
+                </external.container.logFile>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.codehaus.mojo</groupId>
+                            <artifactId>build-helper-maven-plugin</artifactId>
+                            <configuration>
+                                <portNames combine.children="append">
+                                    <portName>memleak.glassfish.adminPort.dynamic</portName>
+                                </portNames>
+                            </configuration>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <version>${project.version}</version>
+                            <configuration>
+                                <adminPort>${memleak.glassfish.adminPort}</adminPort>
+                                <distUrl>${memleak.glassfish.url}</distUrl>
+                                <distTargetLocation>${memleak.glassfish.zip}</distTargetLocation>
+                                <distSubdir>glassfish4</distSubdir>
+                                <maxHeap>${memleak.glassfish.maxheap}</maxHeap>
+                            </configuration>
+                            <executions>
+                                <execution>
+                                    <id>gf4-download</id>
+                                    <goals>
+                                        <goal>downloadGf4</goal>
+                                    </goals>
+                                    <phase>${phase.common.pre-integration-test}</phase>
+                                </execution>
+                                <execution>
+                                    <id>gf4-start-and-deploy</id>
+                                    <goals>
+                                        <goal>startAndDeployGf4</goal>
+                                    </goals>
+                                    <phase>${phase.common.pre-integration-test}</phase>
+                                </execution>
+                                <execution>
+                                    <id>gf4-undeploy-and-stop</id>
+                                    <goals>
+                                        <goal>stopGf4</goal>
+                                    </goals>
+                                    <phase>${phase.common.post-integration-test}</phase>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>wls</id>
+            <properties>
+                <external.container.logFile>
+                    ${project.build.directory}/wls12210/memleak_test_domain/wls.log
+                </external.container.logFile>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <configuration>
+                                <distUrl>${memleak.wls.url}</distUrl>
+                                <distTargetLocation>${memleak.wls.jar}</distTargetLocation>
+                                <distSubdir>wls12210</distSubdir>
+                                <maxHeap>${memleak.wls.maxheap}</maxHeap>
+                            </configuration>
+                            <executions>
+                                <execution>
+                                    <id>wls-download</id>
+                                    <goals>
+                                        <goal>downloadWls</goal>
+                                    </goals>
+                                    <phase>${phase.common.pre-integration-test}</phase>
+                                </execution>
+                                <execution>
+                                    <id>wls-deploy</id>
+                                    <goals>
+                                        <goal>startAndDeployWls</goal>
+                                    </goals>
+                                    <phase>${phase.common.pre-integration-test}</phase>
+                                </execution>
+                                <execution>
+                                    <id>wls-stop</id>
+                                    <goals>
+                                        <goal>stopWls</goal>
+                                    </goals>
+                                    <phase>${phase.common.post-integration-test}</phase>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>tomcat</id>
+            <properties>
+                <external.container.logFile>
+                    ${project.build.directory}/apache-tomcat-${memleak.tomcat.version}/logs/catalina.out
+                </external.container.logFile>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <configuration>
+                                <distUrl>${memleak.tomcat.url}</distUrl>
+                                <distTargetLocation>${memleak.tomcat.zip}</distTargetLocation>
+                                <distSubdir>apache-tomcat-${memleak.tomcat.version}</distSubdir>
+                                <maxHeap>${memleak.tomcat.maxheap}</maxHeap>
+                            </configuration>
+                            <executions>
+                                <execution>
+                                    <id>tomcat-download</id>
+                                    <goals>
+                                        <goal>downloadTomcat</goal>
+                                    </goals>
+                                    <phase>${phase.common.pre-integration-test}</phase>
+                                </execution>
+                                <execution>
+                                    <id>tomcat-deploy</id>
+                                    <goals>
+                                        <goal>startAndDeployTomcat</goal>
+                                    </goals>
+                                    <phase>${phase.common.pre-integration-test}</phase>
+                                </execution>
+                                <execution>
+                                    <id>tomcat-stop</id>
+                                    <goals>
+                                        <goal>stopTomcat</goal>
+                                    </goals>
+                                    <phase>${phase.common.post-integration-test}</phase>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+
+        <!-- profiles that help set ${memleak.tmpdir} based on environment -->
+        <profile>
+            <id>memleak-env-tmpdir</id>
+            <activation>
+                <property>
+                    <name>env.TMPDIR</name>
+                </property>
+            </activation>
+            <properties>
+                <memleak.tmpdir>${env.TMPDIR}</memleak.tmpdir>
+            </properties>
+        </profile>
+        <profile>
+            <id>memleak-env-tmp</id>
+            <activation>
+                <property>
+                    <name>env.TMP</name>
+                </property>
+            </activation>
+            <properties>
+                <memleak.tmpdir>${env.TMP}</memleak.tmpdir>
+            </properties>
+        </profile>
+
+    </profiles>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey.test-framework</groupId>
+                <artifactId>memleak-test-common</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <version>${memleak.jersey.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+</project>
diff --git a/tests/mem-leaks/redeployment/pom.xml b/tests/mem-leaks/redeployment/pom.xml
new file mode 100644
index 0000000..af2f872
--- /dev/null
+++ b/tests/mem-leaks/redeployment/pom.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-memleak-redeployment</name>
+
+    <description>
+        Memory leaks redeployment test.
+
+        This module (and all sub-modules) combine container-runner-maven-plugin goals ('download', 'startAndDeploy', 'stop') with
+        'redeploy' goal which repetitively redeploys and tests with a single http request the deployed application.
+
+        To build all the modules in the redeployment sub-module, run:
+
+        mvn clean install -amd -pl tests/mem-leaks/ -P tomcat,memleak-redeployment -fae
+    </description>
+
+    <properties>
+        <!-- by default, bound to 'none' phase; if 'memleak-redeployment' profile active, they're activated -->
+        <phase.common.pre-integration-test>${phase.redeployment.pre-integration-test}</phase.common.pre-integration-test>
+        <phase.common.post-integration-test>${phase.redeployment.post-integration-test}</phase.common.post-integration-test>
+    </properties>
+
+    <modules>
+        <module>redeployment-leaking-test-app</module>
+        <module>redeployment-hello-world-app-ref</module>
+        <module>redeployment-no-jersey-app</module>
+        <module>redeployment-threadlocals-app</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>memleak-redeployment</id>
+            <properties>
+                <!-- set all the phases for this sub-module tree so that the executions are un-bound from the 'none' phase -->
+                <phase.redeployment.pre-integration-test>pre-integration-test</phase.redeployment.pre-integration-test>
+                <phase.redeployment.integration-test>integration-test</phase.redeployment.integration-test>
+                <phase.redeployment.post-integration-test>post-integration-test</phase.redeployment.post-integration-test>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <configuration>
+                                <!-- these three memleak.redeploy.* properties should be initialized by sub-modules -->
+                                <expectedStatus>${memleak.redeploy.expectedStatus}</expectedStatus>
+                                <redeployCount>${memleak.redeploy.count}</redeployCount>
+                                <requestPathQuery>${memleak.redeploy.requestPathQuery}</requestPathQuery>
+                            </configuration>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-enforcer-plugin</artifactId>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                                    <artifactId>custom-enforcer-rules</artifactId>
+                                    <version>${project.version}</version>
+                                </dependency>
+                            </dependencies>
+                            <executions>
+                                <execution>
+                                    <id>enforce-out-of-memory-did-not-occur</id>
+                                    <goals>
+                                        <goal>enforce</goal>
+                                    </goals>
+                                    <phase>${phase.redeployment.post-integration-test}</phase>
+                                    <configuration>
+                                        <rules>
+                                            <rule implementation="org.glassfish.jersey.test.maven.rule.FilePatternDoesNotExistRule">
+                                                <files>
+                                                    <file>${memleak.jvm.heapdumpdir}/java_pid*.hprof</file>
+                                                </files>
+                                            </rule>
+                                            <rule implementation="org.glassfish.jersey.test.maven.rule.PatternNotMatchedInFileRule">
+                                                <file>${external.container.logFile}</file>
+                                                <pattern>.*java\.lang\.OutOfMemoryError.*</pattern>
+                                            </rule>
+                                        </rules>
+                                        <fail>true</fail>
+                                    </configuration>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+        <profile>
+            <id>wls</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>wls-redeploy</id>
+                                    <goals>
+                                        <goal>redeployWls</goal>
+                                    </goals>
+                                    <phase>${phase.redeployment.integration-test}</phase>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>gf4</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>gf4-redeploy</id>
+                                    <goals>
+                                        <goal>redeployGf4</goal>
+                                    </goals>
+                                    <phase>${phase.redeployment.integration-test}</phase>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>tomcat</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>tomcat-redeploy</id>
+                                    <goals>
+                                        <goal>redeployTomcat</goal>
+                                    </goals>
+                                    <phase>${phase.redeployment.integration-test}</phase>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml b/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml
new file mode 100644
index 0000000..862fb6b
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-hello-world-app-ref/pom.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>redeployment-hello-world-app-ref</artifactId>
+    <name>jersey-tests-memleak-redeployment-hello-world-app-ref</name>
+
+    <description>
+        Maven module that tests :helloworld-webapp:war in order to prevent regression of a problem where a repetitive redeployment
+        may have caused memory leaks.
+
+        To run this test, execute from Jersey root dir:
+
+        [1] Glassfish: mvn install -am -pl :redeployment-hello-world-app-ref -P gf4,memleak-redeployment
+
+        [2] Weblogic: mvn install -am -pl :redeployment-hello-world-app-ref -P wls,memleak-redeployment
+
+        [3] Tomcat: mvn install -am -pl :redeployment-hello-world-app-ref -P tomcat,memleak-redeployment
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>256m</memleak.jvm.maxheap>
+        <memleak.tomcat.maxheap>56m</memleak.tomcat.maxheap>
+
+        <external.container.warPath>${org.glassfish.jersey.examples:helloworld-webapp:war}</external.container.warPath>
+        <external.container.contextRoot>helloworld-webapp</external.container.contextRoot>
+
+        <memleak.redeploy.count>200</memleak.redeploy.count>
+        <memleak.redeploy.expectedStatus>200</memleak.redeploy.expectedStatus>
+        <memleak.redeploy.requestPathQuery>${external.container.contextRoot}/helloworld</memleak.redeploy.requestPathQuery>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>tomcat</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>enforce-threadlocals-left-cleaned</id>
+                                <goals>
+                                    <goal>enforce</goal>
+                                </goals>
+                                <phase>${phase.redeployment.post-integration-test}</phase>
+                                <configuration>
+                                    <!-- assure that Tomcat ThreadLocal memory leak detection messages weren't included in the Tomcat output.
+                                    If so, ThreadLocals aren't kept clean and that's what users do not like (see JERSEY-2890). -->
+                                    <rules>
+                                        <rule implementation="org.glassfish.jersey.test.maven.rule.PatternNotMatchedInFileRule">
+                                            <file>${external.container.logFile}</file>
+                                            <pattern>
+                                                <![CDATA[.*SEVERE.*The web application.*created a ThreadLocal with key.*avoid a probable memory leak.*]]></pattern>
+                                            <maxMatchedLines>3</maxMatchedLines>
+                                        </rule>
+                                    </rules>
+                                    <fail>true</fail>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>memleak-reproducer</id>
+            <properties>
+                <memleak.jersey.version>2.19</memleak.jersey.version>
+            </properties>
+        </profile>
+    </profiles>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                <artifactId>container-runner-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>resolve-maven-dependencies</id>
+                        <goals>
+                            <goal>properties</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.examples</groupId>
+            <artifactId>helloworld-webapp</artifactId>
+            <type>war</type>
+            <version>2.28-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml
new file mode 100644
index 0000000..951b572
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/pom.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>redeployment-leaking-test-app</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-memleak-redeployment-leaking-test-app</name>
+
+    <description>
+        An example application that causes a memory leak in the container where it's being repetitively redeployed.
+
+
+        To run this example, execute from jersey root dir:
+
+        [1] Glassfish: mvn clean install -pl :redeployment-leaking-test-app -P gf4,memleak-leaking-test,memleak-redeployment
+
+        [2] Weblogic: mvn clean install -pl :redeployment-leaking-test-app -P wls,memleak-leaking-test,memleak-redeployment
+
+        [3] Tomcat: mvn clean install -pl :redeployment-leaking-test-app -P tomcat,memleak-leaking-test,memleak-redeployment
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>256m</memleak.jvm.maxheap>
+
+        <memleak.redeploy.count>100</memleak.redeploy.count>
+        <memleak.redeploy.expectedStatus>200</memleak.redeploy.expectedStatus>
+        <memleak.redeploy.requestPathQuery>${external.container.contextRoot}/invoke?size=20000000</memleak.redeploy.requestPathQuery>
+
+    </properties>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                    <artifactId>container-runner-maven-plugin</artifactId>
+                    <configuration>
+                        <method>POST</method>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>enforce-out-of-memory-did-not-occur</id>
+                        <phase>none</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>memleak-test-common</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>memleak-leaking-test</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.mortbay.jetty</groupId>
+                        <artifactId>jetty-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                        <artifactId>container-runner-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>enforce-out-of-memory-did-not-occur</id>
+                                <phase>${phase.redeployment.post-integration-test}</phase>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/DaemonThreadMemoryLeakingResource.java b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/DaemonThreadMemoryLeakingResource.java
new file mode 100644
index 0000000..395de4b
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/DaemonThreadMemoryLeakingResource.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.testleak;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+import javax.inject.Singleton;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * Resource that causes {@link OutOfMemoryError} exception upon repetitive call of {@link #invoke(int)} of an application that is
+ * being redeployed.
+ * <p/>
+ * The purpose of this resource (and the app) is to test whether the memory leaking infrastructure for redeployment scenarios
+ * works.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/")
+@Singleton
+public class DaemonThreadMemoryLeakingResource {
+
+    @POST
+    @Path("invoke")
+    public String invoke(@DefaultValue("1048576") @QueryParam("size") final int size) {
+
+        final Future<?> future = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setDaemon(true).build())
+                .submit(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            System.out.println("Running a Thread!");
+                            final int mbytes = size / (1024 * 1024);
+                            final byte[][] bytes = new byte[mbytes][];
+                            for (int i = 1; i <= mbytes; ++i) {
+                                bytes[i - 1] = new byte[1024 * 1024];
+                                System.out.println("Allocated: " + i + "MB!");
+                            }
+
+                            System.out.println("Memory allocated! Total: " + mbytes + "MB! Sleeping...");
+                            for (int i = 0; i < 1000000; ++i) {
+                                System.out.println("Thread " + Thread.currentThread() + " sleeping!");
+                                Thread.sleep(10000);
+                            }
+                            System.out.println("Freeing: " + size + " of bytes. " + bytes);
+                        } catch (InterruptedException e) {
+                            throw new IllegalStateException("Thread Interrupted!", e);
+                        } catch (Throwable e) {
+                            e.printStackTrace();
+                            throw e;
+                        }
+
+                    }
+                });
+
+        System.out.println("Trying to allocate bytes from the thread itself.");
+        final byte[] bytes = new byte[size];
+        return "Future submitted: " + future + " bytes allocated: " + bytes;
+    }
+
+    @GET
+    @Path("hello")
+    @Produces("text/plain")
+    public String helloWorld() {
+        System.out.println("HELLO WORLD!");
+        return "HELLO WORLD!";
+    }
+
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/TestApplication.java b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/TestApplication.java
new file mode 100644
index 0000000..4d68713
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/TestApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.testleak;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(DaemonThreadMemoryLeakingResource.class);
+    }
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/webapp/WEB-INF/web.xml b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..2b29bd3
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-leaking-test-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+        metadata-complete="true">
+
+    <filter>
+        <filter-name>jersey</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.memleaks.testleak.TestApplication</param-value>
+        </init-param>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>jersey</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml
new file mode 100644
index 0000000..f6f924b
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/pom.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>redeployment-no-jersey-app</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-memleak-redeployment-no-jersey-app</name>
+
+    <description>
+        Reproducer of HK2-247, HK2-264
+
+        On some platforms, redeployment of this application does not cause a memory leak. However, several HK2 instances are left
+        in the container (referenced from ThreadLocals), which is what is tested (on Tomcat only).
+
+        [Execution commands]
+
+        Should not fail: mvn -pl :redeployment-no-jersey-app clean install -P tomcat,memleak-redeployment
+
+        Should fail: mvn -pl :redeployment-no-jersey-app clean install -P tomcat,memleak-redeployment,memleak-reproducer
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>192m</memleak.jvm.maxheap>
+        <external.container.contextRoot>no-jersey-service</external.container.contextRoot>
+
+        <memleak.redeploy.count>500</memleak.redeploy.count>
+        <memleak.redeploy.expectedStatus>200</memleak.redeploy.expectedStatus>
+        <memleak.redeploy.requestPathQuery>${external.container.contextRoot}</memleak.redeploy.requestPathQuery>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>tomcat</id>
+            <properties>
+                <memleak.jvm.maxheap>42m</memleak.jvm.maxheap>
+                <!-- on Tomcat, we can rely on the log record which is detected bellow by maven-enforcer-plugin -->
+                <memleak.redeploy.count>1</memleak.redeploy.count>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-enforcer-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>enforce-threadlocals-left-cleaned</id>
+                                    <goals>
+                                        <goal>enforce</goal>
+                                    </goals>
+                                    <phase>${phase.redeployment.post-integration-test}</phase>
+                                    <configuration>
+                                        <!-- assure that Tomcat ThreadLocal memory leak detection messages weren't included in the Tomcat output.
+                                        If so, ThreadLocals aren't kept clean and that's what users do not like (see JERSEY-2890). -->
+                                        <rules>
+                                            <rule implementation="org.glassfish.jersey.test.maven.rule.PatternNotMatchedInFileRule">
+                                                <file>${external.container.logFile}</file>
+                                                <pattern>
+                                                    <![CDATA[.*SEVERE.*The web application.*created a ThreadLocal with key.*avoid a probable memory leak.*]]></pattern>
+                                                <maxMatchedLines>3</maxMatchedLines>
+                                            </rule>
+                                        </rules>
+                                        <fail>true</fail>
+                                    </configuration>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>memleak-reproducer</id>
+            <properties>
+                <hk2.version>2.5.0-b30</hk2.version>
+            </properties>
+        </profile>
+    </profiles>
+
+    <build>
+
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                <artifactId>container-runner-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+            </plugin>
+        </plugins>
+
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2</groupId>
+            <artifactId>hk2-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2</groupId>
+            <artifactId>hk2-locator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2</groupId>
+            <artifactId>hk2-utils</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/GreetingService.java b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/GreetingService.java
new file mode 100644
index 0000000..9dc4237
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/GreetingService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.nojersey;
+
+import org.jvnet.hk2.annotations.Contract;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Contract
+public interface GreetingService {
+    String getGreeting();
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/GreetingWrapper.java b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/GreetingWrapper.java
new file mode 100644
index 0000000..c4d0c9b
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/GreetingWrapper.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.nojersey;
+
+import org.glassfish.hk2.api.ServiceLocator;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class GreetingWrapper {
+    GreetingService service;
+
+    public GreetingWrapper(ServiceLocator locator) {
+        service = locator.getService(GreetingService.class);
+    }
+
+    public String getInjectedGreeting() {
+        return service.getGreeting();
+    }
+
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/HelloServiceImpl.java b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/HelloServiceImpl.java
new file mode 100644
index 0000000..64aa2bf
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/HelloServiceImpl.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.nojersey;
+
+import org.jvnet.hk2.annotations.Service;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Service
+public class HelloServiceImpl implements GreetingService {
+    @Override
+    public String getGreeting() {
+        return "Hello from MyService";
+    }
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/Main.java b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/Main.java
new file mode 100644
index 0000000..d58b4c6
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.nojersey;
+
+import org.glassfish.hk2.api.DynamicConfiguration;
+import org.glassfish.hk2.api.DynamicConfigurationService;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.hk2.api.ServiceLocatorFactory;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class Main {
+
+    public static void main(String[] args) {
+        ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
+        ServiceLocator locator = factory.create("myLocator");
+
+        DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
+        DynamicConfiguration dc = dcs.createDynamicConfiguration();
+
+        AbstractBinder binder = new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(HelloServiceImpl.class).to(GreetingService.class);
+            }
+        };
+
+        locator.inject(binder);
+        binder.bind(dc);
+
+        dc.commit();
+
+        GreetingWrapper wrapper = new GreetingWrapper(locator);
+        System.out.println("result: " + wrapper.getInjectedGreeting());
+
+    }
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/MyServlet.java b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/MyServlet.java
new file mode 100644
index 0000000..2d6a266
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/java/org/glassfish/jersey/tests/memleaks/nojersey/MyServlet.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.nojersey;
+
+import org.glassfish.hk2.api.DynamicConfiguration;
+import org.glassfish.hk2.api.DynamicConfigurationService;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.hk2.api.ServiceLocatorFactory;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class MyServlet extends HttpServlet {
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
+        ServiceLocator locator = factory.create("myLocator");
+
+        DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
+        DynamicConfiguration dc = dcs.createDynamicConfiguration();
+
+        AbstractBinder binder = new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(HelloServiceImpl.class).to(GreetingService.class);
+            }
+        };
+
+        binder.bind(dc);
+
+        dc.commit();
+
+        GreetingWrapper wrapper = new GreetingWrapper(locator);
+        response.getWriter().write("Greeting: " + wrapper.getInjectedGreeting());
+        locator.shutdown();
+    }
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/webapp/WEB-INF/web.xml b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..e0625b6
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>MyServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.tests.memleaks.nojersey.MyServlet</servlet-class>
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>org.glassfish.jersey.tests.memleaks.nojersey</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>MyServlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/webapp/index.jsp b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/webapp/index.jsp
new file mode 100644
index 0000000..5779d9d
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-no-jersey-app/src/main/webapp/index.jsp
@@ -0,0 +1,25 @@
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+<html>
+<body>
+    <h2>Jersey RESTful Web Application!</h2>
+    <p><a href="webapi/myresource">Jersey resource</a>
+    <p>Visit <a href="http://jersey.java.net">Project Jersey website</a>
+    for more information on Jersey!
+</body>
+</html>
diff --git a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml
new file mode 100644
index 0000000..79188df
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.redeployment</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>redeployment-threadlocals-app</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-memleak-redeployment-threadlocals-app</name>
+
+    <description>
+
+        This project shows that not careful use of ThreadLocal in a thread-pool environment is a risk. In this case, memory leaks
+        may occur under certain circumstances.
+
+        Known cases when memory leaks do happen:
+
+        [1] Tomcat with deployment by using its admin REST API (copying to webapps dir does not cause a memory leak since the
+        initialization of the application is done by a different thread, which might get eventually discarded.)
+
+        [2] Glassfish - probably works due to a fact, that HK2 is not used (as opposed to redeployment-no-jersey-test-app) and the
+        classes that participate on strong references to a GC root are loaded by webapp classloader.
+
+
+        To run this test threadlocals classloader memory leak example, execute from jersey root dir:
+
+        [1] Glassfish: mvn clean install -pl :redeployment-threadlocals-app -P gf4,memleak-leaking-test,memleak-redeployment
+
+        [2] Weblogic: mvn clean install -pl :redeployment-threadlocals-app -P wls,memleak-leaking-test,memleak-redeployment
+
+        [3] Tomcat: mvn clean install -pl :redeployment-threadlocals-app -P tomcat,memleak-leaking-test,memleak-redeployment
+
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>128m</memleak.jvm.maxheap>
+        <external.container.contextRoot>hello</external.container.contextRoot>
+
+        <memleak.redeploy.count>10000</memleak.redeploy.count>
+        <memleak.redeploy.expectedStatus>200</memleak.redeploy.expectedStatus>
+        <memleak.redeploy.requestPathQuery>${external.container.contextRoot}</memleak.redeploy.requestPathQuery>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>tomcat</id>
+            <properties>
+                <memleak.jvm.maxheap>48m</memleak.jvm.maxheap>
+            </properties>
+        </profile>
+        <profile>
+            <id>memleak-leaking-test</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.mortbay.jetty</groupId>
+                        <artifactId>jetty-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                        <artifactId>container-runner-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>enforce-out-of-memory-did-not-occur</id>
+                                <phase>${phase.redeployment.post-integration-test}</phase>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/SomeClass.java b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/SomeClass.java
new file mode 100644
index 0000000..7f36e53
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/SomeClass.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.threadlocal;
+
+/**
+ * A simple class that participates in the class/object reference circle that causes a memory leak.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class SomeClass {
+
+    public static String hello() {
+        return "hello";
+    }
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/StaticReferenceClass.java b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/StaticReferenceClass.java
new file mode 100644
index 0000000..3662e79
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/StaticReferenceClass.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.threadlocal;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+/**
+ * This class is a key to the classloader memory leak since it has a static reference to an object which will refer a {@link
+ * ThreadLocal} instance which refers a class object.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class StaticReferenceClass {
+
+    static final ConcurrentLinkedDeque<ThreadLocal<Class>> STATIC_HOLDER = new ConcurrentLinkedDeque<>();
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/ThreadLocalMemoryLeakServlet.java b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/ThreadLocalMemoryLeakServlet.java
new file mode 100644
index 0000000..a55c85a
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/java/org/glassfish/jersey/tests/memleaks/threadlocal/ThreadLocalMemoryLeakServlet.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.threadlocal;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A servlet that introduces a memory leak with a single call. All the classes loaded by current classloader won't get GCed even
+ * after this application is undeployed.
+ * <p/>
+ * This servlet demonstrates that fix HK2-247 when {@link ThreadLocal} reference is changed from a static to an instance, it does
+ * not always solve memory leak issues as long as a static reference (in this case {@link StaticReferenceClass#STATIC_HOLDER})
+ * holds (even transitively) the {@link ThreadLocal} instance.
+ * <p/>
+ * To revert this case to the simple one (the original one), change the {@link #threadLocal} instance should be static and than no
+ * {@link StaticReferenceClass#STATIC_HOLDER} is needed. The memory leak occurs without any other special actions.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class ThreadLocalMemoryLeakServlet extends HttpServlet {
+
+    /**
+     * This {@link ThreadLocal} reference is held by instance reference and might be GCed; however, the class {@link SomeClass}
+     * has a reference to its classloader which has a reference to {@link StaticReferenceClass} class which has a static reference
+     * to this instance. As a result, this {@link ThreadLocal} instance is never GCed in a thread pool environment.
+     * <p/>
+     * If this field was changed to a static reference, the memory leak would occur even without the {@link
+     * StaticReferenceClass#STATIC_HOLDER} holding this instance (see {@link #doGet(HttpServletRequest, HttpServletResponse)}
+     * method bellow).
+     */
+    final ThreadLocal<Class> threadLocal = new ThreadLocal<Class>() {
+        @Override
+        protected Class initialValue() {
+            return SomeClass.class;
+        }
+    };
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        StaticReferenceClass.STATIC_HOLDER.add(threadLocal);
+        response.getWriter().write("Greeting: " + SomeClass.hello() + "\n");
+        response.getWriter().write("Thread Locals Content: " + threadLocal.get().getCanonicalName() + "\n");
+        response.getWriter().write("Holder size: " + StaticReferenceClass.STATIC_HOLDER.size());
+    }
+}
diff --git a/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/webapp/WEB-INF/web.xml b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..3734180
--- /dev/null
+++ b/tests/mem-leaks/redeployment/redeployment-threadlocals-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+    <servlet>
+        <servlet-name>MyServlet</servlet-name>
+        <servlet-class>org.glassfish.jersey.tests.memleaks.threadlocal.ThreadLocalMemoryLeakServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>MyServlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/mem-leaks/test-cases/bean-param-leak/pom.xml b/tests/mem-leaks/test-cases/bean-param-leak/pom.xml
new file mode 100644
index 0000000..306ee22
--- /dev/null
+++ b/tests/mem-leaks/test-cases/bean-param-leak/pom.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>bean-param-leak</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-memleak-testcase-bean-param-leak</name>
+
+    <description>
+        Reproducer of JERSEY-2800
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>96m</memleak.jvm.maxheap>
+
+        <memleak.wls.maxheap>128m</memleak.wls.maxheap>
+        <memleak.tomcat.maxheap>42m</memleak.tomcat.maxheap>
+        <memleak.jetty.maxheap>70m</memleak.jetty.maxheap>
+
+        <memleak.test.timeout>200000</memleak.test.timeout>
+
+        <memleak.wls.packagingExcludes.weblogic.xml>WEB-INF/weblogic.xml</memleak.wls.packagingExcludes.weblogic.xml>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>memleak-test-common</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <packagingExcludes>${memleak.wls.packagingExcludes.weblogic.xml}</packagingExcludes>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                <artifactId>container-runner-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>memleak-reproducer</id>
+            <properties>
+                <memleak.jersey.version>2.16</memleak.jersey.version>
+                <memleak.wls.packagingExcludes.weblogic.xml />
+            </properties>
+        </profile>
+        <profile>
+            <id>memleak-override-libraries</id>
+            <properties>
+                <memleak.wls.packagingExcludes.weblogic.xml />
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/test-cases/bean-param-leak/src/main/java/org/glassfish/jersey/tests/memleaks/beanparam/BeanParamLeakResource.java b/tests/mem-leaks/test-cases/bean-param-leak/src/main/java/org/glassfish/jersey/tests/memleaks/beanparam/BeanParamLeakResource.java
new file mode 100644
index 0000000..b78f64c
--- /dev/null
+++ b/tests/mem-leaks/test-cases/bean-param-leak/src/main/java/org/glassfish/jersey/tests/memleaks/beanparam/BeanParamLeakResource.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.beanparam;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
+
+import javax.inject.Singleton;
+
+/**
+ * This resource reproduces JERSEY-2800 when {@link #invokeBeanParamInject()} called repetitively.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/beanparam")
+public class BeanParamLeakResource {
+
+    public static class ParameterBean {
+        @Context
+        Request request;
+
+        @QueryParam("q")
+        String q;
+    }
+
+    @BeanParam
+    ParameterBean bean;
+
+    @POST
+    @Path("invoke")
+    public String invokeBeanParamInject() {
+        return bean.q;
+    }
+
+    @GET
+    @Path("helloworld")
+    @Produces("text/plain")
+    public String helloWorld() {
+        return "HELLO WORLD!";
+    }
+
+}
diff --git a/tests/mem-leaks/test-cases/bean-param-leak/src/main/java/org/glassfish/jersey/tests/memleaks/beanparam/TestApplication.java b/tests/mem-leaks/test-cases/bean-param-leak/src/main/java/org/glassfish/jersey/tests/memleaks/beanparam/TestApplication.java
new file mode 100644
index 0000000..5333c45
--- /dev/null
+++ b/tests/mem-leaks/test-cases/bean-param-leak/src/main/java/org/glassfish/jersey/tests/memleaks/beanparam/TestApplication.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.beanparam;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * JAX-RS Application that serves {@link BeanParamLeakResource} class.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(BeanParamLeakResource.class);
+    }
+}
diff --git a/tests/mem-leaks/test-cases/bean-param-leak/src/main/webapp/WEB-INF/web.xml b/tests/mem-leaks/test-cases/bean-param-leak/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..0b31bdb
--- /dev/null
+++ b/tests/mem-leaks/test-cases/bean-param-leak/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+        metadata-complete="true" >
+
+    <filter>
+        <filter-name>jersey</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.memleaks.beanparam.TestApplication</param-value>
+        </init-param>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>jersey</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/mem-leaks/test-cases/bean-param-leak/src/main/webapp/WEB-INF/weblogic.xml b/tests/mem-leaks/test-cases/bean-param-leak/src/main/webapp/WEB-INF/weblogic.xml
new file mode 100644
index 0000000..473e71d
--- /dev/null
+++ b/tests/mem-leaks/test-cases/bean-param-leak/src/main/webapp/WEB-INF/weblogic.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd">
+
+    <wls:container-descriptor>
+        <wls:prefer-application-packages>
+            <!-- jsr311 -->
+            <wls:package-name>javax.ws.rs.*</wls:package-name>
+            <!-- javassist -->
+            <wls:package-name>javassist.*</wls:package-name>
+            <!-- aop repackaged -->
+            <wls:package-name>org.aopalliance.*</wls:package-name>
+
+            <!-- jersey 2 -->
+            <wls:package-name>jersey.repackaged.*</wls:package-name>
+            <wls:package-name>org.glassfish.jersey.*</wls:package-name>
+            <wls:package-name>com.sun.research.ws.wadl.*</wls:package-name>
+
+            <!-- hk2 -->
+            <wls:package-name>org.glassfish.hk2.*</wls:package-name>
+            <wls:package-name>org.jvnet.hk2.*</wls:package-name>
+            <wls:package-name>org.jvnet.tiger_types.*</wls:package-name>
+        </wls:prefer-application-packages>
+
+        <wls:prefer-application-resources>
+            <wls:resource-name>META-INF/services/javax.servlet.ServletContainerInitializer</wls:resource-name>
+            <wls:resource-name>META-INF/services/javax.ws.rs.ext.RuntimeDelegate</wls:resource-name>
+
+            <!-- jersey -->
+            <wls:resource-name>META-INF/services/org.glassfish.jersey.*</wls:resource-name>
+            <wls:resource-name>org.glassfish.jersey.*</wls:resource-name>
+            <wls:resource-name>jersey.repackaged.*</wls:resource-name>
+
+            <!-- hk2 -->
+            <wls:resource-name>META-INF/services/org.glassfish.hk2.*</wls:resource-name>
+        </wls:prefer-application-resources>
+    </wls:container-descriptor>
+
+</wls:weblogic-web-app>
diff --git a/tests/mem-leaks/test-cases/bean-param-leak/src/test/java/org/glassfish/jersey/tests/memleaks/beanparam/BeanParamLeakResourceITCase.java b/tests/mem-leaks/test-cases/bean-param-leak/src/test/java/org/glassfish/jersey/tests/memleaks/beanparam/BeanParamLeakResourceITCase.java
new file mode 100644
index 0000000..b9ffb16
--- /dev/null
+++ b/tests/mem-leaks/test-cases/bean-param-leak/src/test/java/org/glassfish/jersey/tests/memleaks/beanparam/BeanParamLeakResourceITCase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.beanparam;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.memleak.common.AbstractMemoryLeakWebAppTest;
+import org.glassfish.jersey.test.memleak.common.MemoryLeakSucceedingTimeout;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * This is an integration test that reproduces JERSEY-2800 by calling RESTful resource {@link BeanParamLeakResource}
+ * repetitively.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class BeanParamLeakResourceITCase extends AbstractMemoryLeakWebAppTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Rule
+    public Timeout globalTimeout = new MemoryLeakSucceedingTimeout(300_000);
+
+    @Test
+    public void testTheLeakResourceOnce() {
+        final Response response = target("beanparam/invoke").queryParam("q", "hello").request().post(null);
+        Assert.assertEquals(200, response.getStatus());
+        assertEquals("hello", response.readEntity(String.class));
+    }
+
+    @Test
+    public void testTheLeakEndless() {
+
+        for (long i = 0;; i++) {
+            System.out.print("\rRequests made: " + i);
+
+            final Response response = target("beanparam/invoke").queryParam("q", i).request().post(null);
+            if (response.getStatus() != 200) {
+                fail("The server was unable to fulfill the request! This may indicate that OutOfMemory exception occurred.");
+            }
+            assertEquals(Long.toString(i), response.readEntity(String.class));
+
+        }
+    }
+
+}
diff --git a/tests/mem-leaks/test-cases/leaking-test-app/pom.xml b/tests/mem-leaks/test-cases/leaking-test-app/pom.xml
new file mode 100644
index 0000000..7b7f76d
--- /dev/null
+++ b/tests/mem-leaks/test-cases/leaking-test-app/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>leaking-test-app</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-memleak-testcase-leaking-test-app</name>
+
+    <description>
+        The purpose of this test application is to test the test infrastructure whether memory leaks are correctly detected and
+        reported.
+
+        This module should not be included in the standard regression test suite since this application intentionally causes memory
+        leaks.
+
+        In order to include test of this module profile 'memleak-leaking-test' must to be enabled.
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>256m</memleak.jvm.maxheap>
+        <memleak.test.timeout>40000</memleak.test.timeout>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>memleak-test-common</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>memleak-leaking-test</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>build-helper-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.mortbay.jetty</groupId>
+                        <artifactId>jetty-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                        <artifactId>container-runner-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/test-cases/leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/MemoryLeakingResource.java b/tests/mem-leaks/test-cases/leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/MemoryLeakingResource.java
new file mode 100644
index 0000000..4bce46e
--- /dev/null
+++ b/tests/mem-leaks/test-cases/leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/MemoryLeakingResource.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.testleak;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import javax.inject.Singleton;
+
+/**
+ * Resource that causes {@link OutOfMemoryError} exception upon repetitive call of {@link #invoke(int)}.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/")
+@Singleton
+public class MemoryLeakingResource {
+
+    final List<Object> leakingList = new LinkedList<>();
+
+    @POST
+    @Path("invoke")
+    public String invoke(@DefaultValue("1048576") @QueryParam("size") int size) {
+        byte[] bytes = new byte[size];
+        leakingList.add(bytes);
+
+        return String.valueOf(leakingList.size());
+    }
+
+    @GET
+    @Path("hello")
+    @Produces("text/plain")
+    public String helloWorld() {
+        System.out.println("HELLO WORLD!");
+        return "HELLO WORLD!";
+    }
+
+}
diff --git a/tests/mem-leaks/test-cases/leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/TestApplication.java b/tests/mem-leaks/test-cases/leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/TestApplication.java
new file mode 100644
index 0000000..b0435e5
--- /dev/null
+++ b/tests/mem-leaks/test-cases/leaking-test-app/src/main/java/org/glassfish/jersey/tests/memleaks/testleak/TestApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.testleak;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(MemoryLeakingResource.class);
+    }
+}
diff --git a/tests/mem-leaks/test-cases/leaking-test-app/src/main/webapp/WEB-INF/web.xml b/tests/mem-leaks/test-cases/leaking-test-app/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..7f90c9a
--- /dev/null
+++ b/tests/mem-leaks/test-cases/leaking-test-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+        metadata-complete="true" >
+
+    <filter>
+        <filter-name>jersey</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.memleaks.testleak.TestApplication</param-value>
+        </init-param>
+    </filter>
+
+
+    <filter-mapping>
+        <filter-name>jersey</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/mem-leaks/test-cases/leaking-test-app/src/test/java/org/glassfish/jersey/tests/memleaks/testleak/MemoryLeakingResourceITCase.java b/tests/mem-leaks/test-cases/leaking-test-app/src/test/java/org/glassfish/jersey/tests/memleaks/testleak/MemoryLeakingResourceITCase.java
new file mode 100644
index 0000000..3b7b9ab
--- /dev/null
+++ b/tests/mem-leaks/test-cases/leaking-test-app/src/test/java/org/glassfish/jersey/tests/memleaks/testleak/MemoryLeakingResourceITCase.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.testleak;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.memleak.common.AbstractMemoryLeakWebAppTest;
+import org.glassfish.jersey.test.memleak.common.MemoryLeakSucceedingTimeout;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import static org.junit.Assert.fail;
+
+/**
+ * Performs the test of a memory leaking RESTful resource.
+ * <p/>
+ * This test class should be used to test the memory leaking test infrastructure.
+ */
+public class MemoryLeakingResourceITCase extends AbstractMemoryLeakWebAppTest {
+
+    @Rule
+    public Timeout globalTimeout = new MemoryLeakSucceedingTimeout(100_000);
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    /**
+     * This test runs i single request; as such, it may or may not cause a memory leak (and end with a failure).
+     */
+    @Test
+    public void testTheLeak() {
+
+        final WebTarget webTarget = target("invoke").queryParam("size", 1024 * 1024);
+        System.out.println(webTarget.getUri());
+        final Response response = webTarget.request().post(null);
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    /**
+     * This test should fail after certain period of time (depending on the JVM settings too).
+     */
+    @Test
+    public void testTheLeakEndless() {
+
+        while (true) {
+            System.out.print(".");
+            final Response response = target("invoke").queryParam("size", 1024 * 1024).request().post(null);
+
+            if (response.getStatus() != 200) {
+                fail("The server was unable to fulfill the request! This may indicate that OutOfMemory exception occurred.");
+            }
+        }
+    }
+
+}
diff --git a/tests/mem-leaks/test-cases/pom.xml b/tests/mem-leaks/test-cases/pom.xml
new file mode 100644
index 0000000..c7fce61
--- /dev/null
+++ b/tests/mem-leaks/test-cases/pom.xml
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-memleak-testcase</name>
+
+    <description>
+        Memory leaks test cases.
+
+        Tests in this maven submodule can be either:
+
+        [1] Simple tests executed by maven-surefire-plugin in a dedicated JVM. These tests should attempt to cause a memory leak
+        directly. The heap can be configured by means of 'memleak.jvm.maxheap' property. It can be combined with other
+        infrastructure which is described bellow.
+
+        [2] Integration tests executed by maven-surefire-plugin. These tests should test against Jersey application deployed to a
+        servlet container. The state of the servlet container (e.g., whether OutOfMemoryError occurred) should be inspected by
+        infrastructure described bellow.
+
+        TEST INFRASTRUCTURE
+
+        The test infrastructure is configured from Maven and the tests should be executed by Maven. Normally, test-cases shall
+        override maven properties defined in this pom and declare required maven plugins in their build section. All the maven
+        plugin executions will be configured from this pom.
+
+        [a] Container Runners (org.glassfish.jersey.test-framework.maven:container-runner-maven-plugin) feature Weblogic and
+        Glassfish4 with functionality: download, start, deploy and stop.
+
+        [b] Maven Enforce Custom Rules (org.glassfish.jersey.test-framework.maven:custom-enforcer-rules) that can enforce
+        non-existent Java Heap dumps files in a certain path or no OutOfMemoryError errors in a log file.
+
+        [c] Jetty execution in a dedicated JVM with configured to dump heap on OutOfMemoryError errors.
+
+        [d] Common Memleak JUnit test infrastructure that provides means to stop a test after a certain period of time and mark
+        the test as successful as well as to perform heap dumps from the Java code or to inspect log files for OutOfMemoryError
+        errors.
+
+        EXAMPLE OF EXECUTION
+
+        (executes all memleak test cases)
+        mvn clean install -amd -pl tests/mem-leaks -P gf4,memleak-test-cases -fae
+
+        (executes shutdown hook leak single test case)
+        mvn clean install -am -pl tests/mem-leaks/test-cases/shutdown-hook-leak -P gf4,memleak-test-cases -fae
+    </description>
+
+    <modules>
+        <module>bean-param-leak</module>
+        <module>shutdown-hook-leak</module>
+        <module>shutdown-hook-leak-client</module>
+        <module>leaking-test-app</module>
+    </modules>
+
+    <properties>
+        <memleak.test.timeout>300000</memleak.test.timeout>
+        <memleak.jetty.maxheap>${memleak.jvm.maxheap}</memleak.jetty.maxheap>
+
+        <!-- by default, bound to 'none' phase; if 'memleak-test-cases' profile active, they're activated -->
+        <phase.common.pre-integration-test>${phase.tests.pre-integration-test}</phase.common.pre-integration-test>
+        <phase.common.post-integration-test>${phase.tests.post-integration-test}</phase.common.post-integration-test>
+
+        <phase.tests.pre-integration-test.jetty>${phase.tests.pre-integration-test}</phase.tests.pre-integration-test.jetty>
+        <phase.tests.post-integration-test.jetty>${phase.tests.post-integration-test}</phase.tests.post-integration-test.jetty>
+    </properties>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <configuration>
+                        <systemPropertyVariables>
+                            <jersey.config.test.memleak.timeout>${memleak.test.timeout}</jersey.config.test.memleak.timeout>
+                            <jersey.config.test.container.factory>${external.container.factory}
+                            </jersey.config.test.container.factory>
+                            <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            <jersey.config.test.container.contextRoot>${external.container.contextRoot}
+                            </jersey.config.test.container.contextRoot>
+                            <jersey.config.test.container.logfile>${external.container.logFile}
+                            </jersey.config.test.container.logfile>
+                        </systemPropertyVariables>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <!-- the 'default' id is bound to 'none' because it's not possible to split the goals and re-bind them
+                            while keeping the 'default' id -->
+                            <id>default</id>
+                            <phase>none</phase>
+                        </execution>
+                        <execution>
+                            <id>re-bound-integration-test</id>
+                            <goals>
+                                <goal>integration-test</goal>
+                            </goals>
+                            <phase>${phase.tests.integration-test}</phase>
+                        </execution>
+                        <execution>
+                            <id>re-bound-verify</id>
+                            <goals>
+                                <goal>verify</goal>
+                            </goals>
+                            <phase>${phase.tests.verify}</phase>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <configuration>
+                        <systemPropertyVariables>
+                            <jersey.config.test.memleak.timeout>${memleak.test.timeout}</jersey.config.test.memleak.timeout>
+                            <jersey.config.test.memleak.heapDumpPath>${project.build.directory}
+                            </jersey.config.test.memleak.heapDumpPath>
+                            <jersey.config.test.container.factory>${external.container.factory}
+                            </jersey.config.test.container.factory>
+                            <jersey.config.test.container.port>${external.container.port}</jersey.config.test.container.port>
+                            <jersey.config.test.container.contextRoot>${external.container.contextRoot}
+                            </jersey.config.test.container.contextRoot>
+                        </systemPropertyVariables>
+                        <argLine>-Xmx${memleak.jvm.maxheap} -XX:+HeapDumpOnOutOfMemoryError
+                            -XX:HeapDumpPath=${memleak.jvm.heapdumpdir} -XX:GCTimeLimit=20 -XX:GCHeapFreeLimit=30
+                        </argLine>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <id>default-test</id>
+                            <goals>
+                                <goal>test</goal>
+                            </goals>
+                            <phase>${phase.tests.test}</phase>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>memleak-test-cases</id>
+            <properties>
+                <!-- set all the phases for this sub-module tree so that the executions are un-bound from the 'none' phase -->
+                <phase.tests.test>test</phase.tests.test>
+                <phase.tests.pre-integration-test>pre-integration-test</phase.tests.pre-integration-test>
+                <phase.tests.integration-test>integration-test</phase.tests.integration-test>
+                <phase.tests.post-integration-test>post-integration-test</phase.tests.post-integration-test>
+                <phase.tests.verify>verify</phase.tests.verify>
+            </properties>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.mortbay.jetty</groupId>
+                            <artifactId>jetty-maven-plugin</artifactId>
+                            <configuration>
+                                <skip>${skip.tests}</skip>
+                                <scanIntervalSeconds>10</scanIntervalSeconds>
+                                <stopPort>9999</stopPort>
+                                <stopKey>STOP</stopKey>
+                                <contextPath>/${external.container.contextRoot}</contextPath>
+                                <jvmArgs>-Xms64m -Xmx${memleak.jetty.maxheap} -XX:PermSize=128m -XX:MaxPermSize=256m
+                                    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${memleak.jvm.heapdumpdir}
+                                    -Djetty.port=${external.container.port} -Djersey.config.test.memleak.jetty.magicRunnerIdentifier
+                                    -Dorg.slf4j.simpleLogger.logFile=${external.container.logFile}
+                                </jvmArgs>
+                                <waitForChild>false</waitForChild>
+                            </configuration>
+                            <executions>
+                                <execution>
+                                    <id>start-jetty</id>
+                                    <phase>${phase.tests.pre-integration-test.jetty}</phase>
+                                    <goals>
+                                        <goal>run-forked</goal>
+                                    </goals>
+                                </execution>
+                                <execution>
+                                    <id>stop-jetty</id>
+                                    <phase>${phase.tests.post-integration-test.jetty}</phase>
+                                    <goals>
+                                        <goal>stop</goal>
+                                    </goals>
+                                </execution>
+                            </executions>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.slf4j</groupId>
+                                    <artifactId>slf4j-simple</artifactId>
+                                    <version>1.7.5</version>
+                                </dependency>
+                            </dependencies>
+                        </plugin>
+
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-enforcer-plugin</artifactId>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                                    <artifactId>custom-enforcer-rules</artifactId>
+                                    <version>${project.version}</version>
+                                </dependency>
+                            </dependencies>
+                            <executions>
+                                <execution>
+                                    <id>enforce-out-of-memory-did-not-occur</id>
+                                    <goals>
+                                        <goal>enforce</goal>
+                                    </goals>
+                                    <phase>${phase.tests.post-integration-test}</phase>
+                                    <configuration>
+                                        <rules>
+                                            <rule implementation="org.glassfish.jersey.test.maven.rule.FilePatternDoesNotExistRule">
+                                                <files>
+                                                    <file>${memleak.jvm.heapdumpdir}/java_pid*.hprof</file>
+                                                </files>
+                                            </rule>
+                                            <rule implementation="org.glassfish.jersey.test.maven.rule.PatternNotMatchedInFileRule">
+                                                <file>${external.container.logFile}</file>
+                                                <pattern>.*java\.lang\.OutOfMemoryError.*</pattern>
+                                            </rule>
+                                        </rules>
+                                        <fail>true</fail>
+                                    </configuration>
+                                </execution>
+                            </executions>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                            <artifactId>container-runner-maven-plugin</artifactId>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
+        <profile>
+            <id>gf4</id>
+            <properties>
+                <phase.tests.pre-integration-test.jetty>none</phase.tests.pre-integration-test.jetty>
+                <phase.tests.post-integration-test.jetty>none</phase.tests.post-integration-test.jetty>
+            </properties>
+        </profile>
+        <profile>
+            <id>wls</id>
+            <properties>
+                <phase.tests.pre-integration-test.jetty>none</phase.tests.pre-integration-test.jetty>
+                <phase.tests.post-integration-test.jetty>none</phase.tests.post-integration-test.jetty>
+            </properties>
+        </profile>
+        <profile>
+            <id>tomcat</id>
+            <properties>
+                <phase.tests.pre-integration-test.jetty>none</phase.tests.pre-integration-test.jetty>
+                <phase.tests.post-integration-test.jetty>none</phase.tests.post-integration-test.jetty>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml b/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml
new file mode 100644
index 0000000..b554b06
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak-client/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>shutdown-hook-leak-client</artifactId>
+    <name>jersey-tests-memleak-testcase-shutdown-hook-leak-client</name>
+
+    <description>
+        This web project reproduces tickets: JERSEY-2688 JERSEY-2786
+    </description>
+
+    <properties>
+        <memleak.jvm.maxheap>128m</memleak.jvm.maxheap>
+        <memleak.test.timeout>30000</memleak.test.timeout>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>memleak-test-common</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>memleak-reproducer</id>
+            <properties>
+                <memleak.jersey.version>2.17</memleak.jersey.version>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak-client/src/test/java/org/glassfish/jersey/tests/memleaks/shutdownleak/client/EndlessShutdownHookLeakTest.java b/tests/mem-leaks/test-cases/shutdown-hook-leak-client/src/test/java/org/glassfish/jersey/tests/memleaks/shutdownleak/client/EndlessShutdownHookLeakTest.java
new file mode 100644
index 0000000..330b76e
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak-client/src/test/java/org/glassfish/jersey/tests/memleaks/shutdownleak/client/EndlessShutdownHookLeakTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.shutdownleak.client;
+
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.test.memleak.common.AbstractMemoryLeakSimpleTest;
+import org.glassfish.jersey.test.memleak.common.MemoryLeakSucceedingTimeout;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+
+/**
+ * Reproducer for JERSEY-2786.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class EndlessShutdownHookLeakTest extends AbstractMemoryLeakSimpleTest {
+
+    private static final Logger LOGGER = Logger.getLogger(EndlessShutdownHookLeakTest.class.getName());
+
+    @Rule
+    public Timeout globalTimeout = new MemoryLeakSucceedingTimeout();
+
+    final Client client = ClientBuilder.newClient();
+    final WebTarget target = client.target("http://example.com");
+
+    public EndlessShutdownHookLeakTest() {
+        LOGGER.fine("Preparing...");
+        for (int i = 0; i < 1000; ++i) {
+            shutdownHookLeakIteration();
+        }
+    }
+
+    @Test
+    public void testShutdownHookDoesNotLeak() throws Exception {
+        while (true) {
+            shutdownHookLeakIteration();
+        }
+    }
+
+    private void shutdownHookLeakIteration() {
+        System.out.print(".");
+        WebTarget target2 = target.property("Washington", "Irving");
+        Builder req = target2.request().property("how", "now");
+        req.buildGet().property("Irving", "Washington");
+    }
+
+}
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml b/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml
new file mode 100644
index 0000000..24386bc
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.memleaks.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>shutdown-hook-leak</artifactId>
+    <packaging>war</packaging>
+    <name>jersey-tests-memleak-testcase-shutdown-hook-leak</name>
+
+    <properties>
+        <memleak.jvm.maxheap>128m</memleak.jvm.maxheap>
+        <memleak.tomcat.maxheap>128m</memleak.tomcat.maxheap>
+        <memleak.test.timeout>120000</memleak.test.timeout>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>memleak-test-common</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.mortbay.jetty</groupId>
+                <artifactId>jetty-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.jersey.test-framework.maven</groupId>
+                <artifactId>container-runner-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>memleak-reproducer</id>
+            <properties>
+                <memleak.jersey.version>2.15</memleak.jersey.version>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/java/org/glassfish/jersey/tests/memleaks/shutdownhook/ClientShutdownLeakResource.java b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/java/org/glassfish/jersey/tests/memleaks/shutdownhook/ClientShutdownLeakResource.java
new file mode 100644
index 0000000..ff2a8db
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/java/org/glassfish/jersey/tests/memleaks/shutdownhook/ClientShutdownLeakResource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.shutdownhook;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+
+import javax.inject.Singleton;
+
+/**
+ * This resource reproduces JERSEY-2786 when {@link #invokeClient()} called repetitively.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("client")
+@Singleton
+public class ClientShutdownLeakResource {
+
+    final Client client = ClientBuilder.newClient();
+    final WebTarget target = client.target("http://example.com");
+
+    @POST
+    @Path("invoke")
+    public String invokeClient() {
+
+        WebTarget target2 = target.property("Washington", "Irving");
+        Invocation.Builder req = target2.request().property("how", "now");
+        req.buildGet().property("Irving", "Washington");
+
+        return target.toString();
+
+    }
+
+    @GET
+    @Path("helloworld")
+    @Produces("text/plain")
+    public String helloWorld() {
+        return "HELLO WORLD!";
+    }
+
+}
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/java/org/glassfish/jersey/tests/memleaks/shutdownhook/TestApplication.java b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/java/org/glassfish/jersey/tests/memleaks/shutdownhook/TestApplication.java
new file mode 100644
index 0000000..44ddb33
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/java/org/glassfish/jersey/tests/memleaks/shutdownhook/TestApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.shutdownhook;
+
+import org.glassfish.jersey.server.ResourceConfig;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class TestApplication extends ResourceConfig {
+
+    public TestApplication() {
+        register(ClientShutdownLeakResource.class);
+    }
+}
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/webapp/WEB-INF/web.xml b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..b36a8d0
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+        metadata-complete="true" >
+
+    <filter>
+        <filter-name>jersey</filter-name>
+        <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.memleaks.shutdownhook.TestApplication</param-value>
+        </init-param>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>jersey</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/tests/mem-leaks/test-cases/shutdown-hook-leak/src/test/java/org/glassfish/jersey/tests/memleaks/shutdownhook/ShutdownLeakResourceITCase.java b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/test/java/org/glassfish/jersey/tests/memleaks/shutdownhook/ShutdownLeakResourceITCase.java
new file mode 100644
index 0000000..6d5554f
--- /dev/null
+++ b/tests/mem-leaks/test-cases/shutdown-hook-leak/src/test/java/org/glassfish/jersey/tests/memleaks/shutdownhook/ShutdownLeakResourceITCase.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2018 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.memleaks.shutdownhook;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.memleak.common.AbstractMemoryLeakWebAppTest;
+import org.glassfish.jersey.test.memleak.common.MemoryLeakSucceedingTimeout;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import static org.junit.Assert.fail;
+
+/**
+ * This is an integration test that reproduces JERSEY-2786 by calling RESTful resource {@link ClientShutdownLeakResource}
+ * repetitively.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class ShutdownLeakResourceITCase extends AbstractMemoryLeakWebAppTest {
+
+    @Override
+    protected Application configure() {
+        return new TestApplication();
+    }
+
+    @Rule
+    public Timeout globalTimeout = new MemoryLeakSucceedingTimeout(20_000);
+
+    @Test
+    public void testTheLeakResourceOnce() {
+        final Response response = target("client/invoke").request().post(null);
+        Assert.assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testTheLeakEndless() {
+
+        while (true) {
+            System.out.print(".");
+            final Response response = target("client/invoke").request().post(null);
+
+            if (response.getStatus() != 200) {
+                fail("The server was unable to fulfill the request! This may indicate that OutOfMemory exception occurred.");
+            }
+        }
+    }
+
+}
diff --git a/tests/osgi/functional/pom.xml b/tests/osgi/functional/pom.xml
new file mode 100644
index 0000000..2eb1ba4
--- /dev/null
+++ b/tests/osgi/functional/pom.xml
@@ -0,0 +1,417 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.osgi</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-tests-osgi-functional</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-osgi-functional</name>
+    <description>
+        Functional Jersey OSGi tests
+    </description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>test-compile</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>testCompile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>process-test-resources</phase>
+                        <goals>
+                            <goal>testResources</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>test</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- use pax exam maven plugin -->
+            <plugin>
+                <groupId>org.ops4j.pax.exam</groupId>
+                <artifactId>maven-paxexam-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-config</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit4</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-container-forked</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-junit-extender-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.exam</groupId>
+            <artifactId>pax-exam-link-mvn</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.url</groupId>
+            <artifactId>pax-url-aether</artifactId>
+            <version>1.6.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-servlet</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-servlet</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- dependecies for OSGi test with Apache connector -->
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-apache-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-api</artifactId>
+            <scope>test</scope>
+            <version>1.8.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore-osgi</artifactId>
+            <scope>test</scope>
+            <version>4.4.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient-osgi</artifactId>
+            <scope>test</scope>
+            <version>${httpclient.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson1</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jettison</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-processing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-bean-validation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-entity-filtering</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-multipart</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-sse</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.ext</groupId>
+            <artifactId>jersey-proxy-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.bundles</groupId>
+            <artifactId>jaxrs-ri</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>jetty-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>servlet-api-2.5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml</groupId>
+            <artifactId>classmate</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.el</groupId>
+            <artifactId>javax.el-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>javax.json</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish</groupId>
+            <artifactId>jsonp-jaxrs</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2.external</groupId>
+            <artifactId>aopalliance-repackaged</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.module</groupId>
+            <artifactId>jackson-module-jaxb-annotations</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-base</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-jaxrs</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-xc</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.6.4</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>testsSkipJdk6</id>
+            <activation>
+                <jdk>1.6</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <excludes>
+                                <exclude>**/PackageScanningTest.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/AbstractJsonOsgiIntegrationTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/AbstractJsonOsgiIntegrationTest.java
new file mode 100644
index 0000000..7d3a5dc
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/AbstractJsonOsgiIntegrationTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Abstract JSON OSGi integration test.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public abstract class AbstractJsonOsgiIntegrationTest {
+
+    private static final String CONTEXT = "/jersey";
+    private static final URI baseUri = UriBuilder.fromUri("http://localhost").port(Helper.getPort()).path(CONTEXT).build();
+
+    protected abstract Feature getJsonProviderFeature();
+
+    @Test
+    public void testJson() throws Exception {
+        final Feature jsonProviderFeature = getJsonProviderFeature();
+        final Client client = ClientBuilder.newClient();
+        final ResourceConfig resourceConfig = new ResourceConfig(JsonResource.class);
+
+        if (jsonProviderFeature != null) {
+            client.register(jsonProviderFeature);
+            resourceConfig.register(jsonProviderFeature);
+        }
+
+        HttpServer server = null;
+        try {
+            server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+            final String result = client.target(baseUri).path("/json").request(MediaType.APPLICATION_JSON).get(String.class);
+
+            System.out.println("RESULT = " + result);
+            assertThat(result, containsString("Jim"));
+        } finally {
+            if (server != null) {
+                server.shutdownNow();
+            }
+        }
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/ApacheOsgiIntegrationTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/ApacheOsgiIntegrationTest.java
new file mode 100644
index 0000000..77e376e
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/ApacheOsgiIntegrationTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@RunWith(PaxExam.class)
+public class ApacheOsgiIntegrationTest {
+
+    private static final URI baseUri = UriBuilder.fromUri("http://localhost").port(Helper.getPort()).path("/jersey").build();
+
+    @Configuration
+    public static Option[] configuration() {
+        final List<Option> options = Helper.getCommonOsgiOptions();
+        options.addAll(Helper.expandedList(
+                mavenBundle().groupId("org.ops4j.pax.logging").artifactId("pax-logging-api").versionAsInProject(),
+                mavenBundle().groupId("org.apache.httpcomponents").artifactId("httpcore-osgi").versionAsInProject(),
+                mavenBundle().groupId("org.apache.httpcomponents").artifactId("httpclient-osgi").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.connectors").artifactId("jersey-apache-connector")
+                        .versionAsInProject()
+
+        ));
+        return Helper.asArray(options);
+    }
+
+    @Path("/apacheOsgiTest")
+    public static class ApacheOsgiTestResource {
+
+        @GET
+        public String getMe() {
+            return "OK";
+        }
+    }
+
+    @Test
+    public void testSimpleResource() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(ApacheOsgiTestResource.class);
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        final ClientConfig clientConfig = new ClientConfig();
+        clientConfig.connectorProvider(new ApacheConnectorProvider());
+        final Client c = ClientBuilder.newClient(clientConfig);
+
+        final Response response = c.target(baseUri).path("/apacheOsgiTest").request().buildGet().invoke();
+
+        final String result = response.readEntity(String.class);
+        assertEquals("OK", result);
+        server.shutdownNow();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BasicOsgiIntegrationTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BasicOsgiIntegrationTest.java
new file mode 100644
index 0000000..104347f
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BasicOsgiIntegrationTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public class BasicOsgiIntegrationTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+        return Helper.asArray(options);
+    }
+
+    @Path("/super-simple")
+    public static class SuperSimpleResource {
+
+        @GET
+        public String getMe() {
+            return "OK";
+        }
+    }
+
+    @Test
+    public void testSimpleResource() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(SuperSimpleResource.class);
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        Client c = ClientBuilder.newClient();
+        final Response response = c.target(baseUri).path("/super-simple").request().buildGet().invoke();
+
+        String result = response.readEntity(String.class);
+        System.out.println("RESULT = " + result);
+
+        assertEquals("OK", result);
+
+        server.shutdownNow();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationResource.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationResource.java
new file mode 100644
index 0000000..f67d8d4
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationResource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("bean-validation")
+public class BeanValidationResource {
+
+    @POST
+    public String post(@NotNull @FormParam("formParam") final String formParam) {
+        return formParam;
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
new file mode 100644
index 0000000..fc8076a
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.validation.ValidationFeature;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * Basic test of Bean Validation.
+ *
+ * @author Michal Gajdos
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@RunWith(PaxExam.class)
+public class BeanValidationTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder.fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+
+        options.addAll(Helper.expandedList(
+                // for debug purposes
+                // PaxRunnerOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                // validation
+                mavenBundle().groupId("org.glassfish.jersey.ext").artifactId("jersey-bean-validation").versionAsInProject(),
+                mavenBundle().groupId("org.hibernate").artifactId("hibernate-validator").versionAsInProject(),
+                mavenBundle().groupId("org.jboss.logging").artifactId("jboss-logging").versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml").artifactId("classmate").versionAsInProject(),
+                mavenBundle().groupId("javax.el").artifactId("javax.el-api").versionAsInProject()
+        ));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @Test
+    public void testBeanValidationResourceFeature() throws Exception {
+        _test(400, true, false);
+    }
+
+    @Test
+    public void testBeanValidationResourceAutoDiscovery() throws Exception {
+        _test(400, false, false);
+    }
+
+    @Test
+    public void testBeanValidationResourceManualRegistration() throws Exception {
+        _test(400, true, true);
+    }
+
+    @Test
+    public void testBeanValidationResourceNoValidationFeature() throws Exception {
+        // Even though properties are disabled BV is registered.
+        _test(400, false, true);
+    }
+
+    protected void _test(final int expectedResponseCode,
+                         final boolean registerFeature,
+                         final boolean disableMetainfServicesLookup) {
+        final ResourceConfig resourceConfig = new ResourceConfig(BeanValidationResource.class);
+        if (registerFeature) {
+            resourceConfig.register(ValidationFeature.class);
+        }
+        if (disableMetainfServicesLookup) {
+            resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, Boolean.TRUE);
+
+            resourceConfig.register(org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainerProvider.class);
+        }
+
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        final Form form = new Form();
+        final String formValue = "formValue";
+        form.asMap().add("formParam", formValue);
+
+        final Client client = ClientBuilder.newClient();
+        final String entity = client.target(baseUri)
+                .path("/bean-validation")
+                .request()
+                .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE), String.class);
+
+        assertEquals(formValue, entity);
+
+        final Response response = client.target(baseUri)
+                .path("/bean-validation")
+                .request()
+                .post(Entity.entity(new Form(), MediaType.APPLICATION_FORM_URLENCODED_TYPE));
+
+        assertEquals(expectedResponseCode, response.getStatus());
+
+        server.shutdownNow();
+    }
+
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JaxRsRiBundleTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JaxRsRiBundleTest.java
new file mode 100644
index 0000000..34c9977
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JaxRsRiBundleTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * Basic test of JAX-RS RI bundle.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public class JaxRsRiBundleTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions(false);
+
+        options.addAll(Helper.expandedList(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                mavenBundle().groupId("org.glassfish.jersey.bundles").artifactId("jaxrs-ri").versionAsInProject(),
+
+                mavenBundle().groupId("org.mortbay.jetty").artifactId("servlet-api-2.5").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http-servlet").versionAsInProject()
+        ));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @Test
+    public void testSimpleResource() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(SimpleResource.class);
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        final Client client = ClientBuilder.newClient();
+        final String response = client.target(baseUri).path("/simple").request().get(String.class);
+
+        System.out.println("RESULT = " + response);
+        assertEquals("OK", response);
+
+        server.shutdownNow();
+    }
+
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJackson1Test.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJackson1Test.java
new file mode 100644
index 0000000..8552fc1
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJackson1Test.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014, 2018 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.osgi.test.basic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+
+import org.glassfish.jersey.jackson1.Jackson1Feature;
+import org.glassfish.jersey.osgi.test.util.Helper;
+
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JsonJackson1Test extends AbstractJsonOsgiIntegrationTest {
+
+    @Configuration
+    public static Option[] configuration() {
+        final List<Option> options = new ArrayList<>();
+
+        options.addAll(Helper.getCommonOsgiOptions());
+        options.addAll(Helper.expandedList(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-jackson1").versionAsInProject(),
+
+                // jersey-json dependencies
+                mavenBundle().groupId("org.codehaus.jackson").artifactId("jackson-core-asl").versionAsInProject(),
+                mavenBundle().groupId("org.codehaus.jackson").artifactId("jackson-mapper-asl").versionAsInProject(),
+                mavenBundle().groupId("org.codehaus.jackson").artifactId("jackson-jaxrs").versionAsInProject(),
+                mavenBundle().groupId("org.codehaus.jackson").artifactId("jackson-xc").versionAsInProject()
+        ));
+
+        return Helper.asArray(options);
+    }
+
+    @Override
+    protected Feature getJsonProviderFeature() {
+        return new Jackson1Feature();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java
new file mode 100644
index 0000000..644d053
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJacksonTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010, 2018 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.osgi.test.basic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.osgi.test.util.Helper;
+
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JsonJacksonTest extends AbstractJsonOsgiIntegrationTest {
+
+    @Configuration
+    public static Option[] configuration() {
+        final List<Option> options = new ArrayList<>();
+
+        options.addAll(Helper.getCommonOsgiOptions());
+        options.addAll(Helper.expandedList(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-jackson").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.ext").artifactId("jersey-entity-filtering").versionAsInProject(),
+
+                // jersey-json dependencies
+                mavenBundle().groupId("com.fasterxml.jackson.core").artifactId("jackson-core").versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml.jackson.core").artifactId("jackson-databind").versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml.jackson.core").artifactId("jackson-annotations").versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml.jackson.jaxrs").artifactId("jackson-jaxrs-base").versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml.jackson.jaxrs").artifactId("jackson-jaxrs-json-provider")
+                        .versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml.jackson.module").artifactId("jackson-module-jaxb-annotations")
+                        .versionAsInProject()
+        ));
+
+        return Helper.asArray(options);
+    }
+
+    @Override
+    protected Feature getJsonProviderFeature() {
+        return new JacksonFeature();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJettisonTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJettisonTest.java
new file mode 100644
index 0000000..afa5cc0
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonJettisonTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+
+import org.glassfish.jersey.jettison.JettisonFeature;
+import org.glassfish.jersey.osgi.test.util.Helper;
+
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.Configuration;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JsonJettisonTest extends AbstractJsonOsgiIntegrationTest {
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = new ArrayList<Option>();
+
+        options.addAll(Helper.getCommonOsgiOptions());
+        options.addAll(Helper.expandedList(
+                // jersey-json dependencies
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-jettison").versionAsInProject(),
+                mavenBundle().groupId("org.codehaus.jettison").artifactId("jettison").versionAsInProject()
+        ));
+
+        return Helper.asArray(options);
+    }
+
+    @Override
+    protected Feature getJsonProviderFeature() {
+        return new JettisonFeature();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java
new file mode 100644
index 0000000..d8bc1f0
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonMoxyTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.osgi.test.util.Helper;
+
+import org.eclipse.persistence.jaxb.BeanValidationMode;
+import org.eclipse.persistence.jaxb.MarshallerProperties;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import static org.ops4j.pax.exam.CoreOptions.bootDelegationPackage;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JsonMoxyTest extends AbstractJsonOsgiIntegrationTest {
+
+    @Configuration
+    public static Option[] configuration() {
+        final List<Option> options = new ArrayList<>();
+
+        options.addAll(Helper.getCommonOsgiOptions());
+        options.addAll(Helper.expandedList(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                bootDelegationPackage("javax.xml.bind"),
+                bootDelegationPackage("javax.xml.bind.*"),
+                // validation
+                bootDelegationPackage("javax.xml.parsers"),
+                bootDelegationPackage("javax.xml.parsers.*"),
+
+                // moxy dependencies
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-moxy").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.ext").artifactId("jersey-entity-filtering").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.moxy").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.core").versionAsInProject(),
+                mavenBundle().groupId("org.eclipse.persistence").artifactId("org.eclipse.persistence.asm").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish").artifactId("javax.json").versionAsInProject(),
+
+                // validation
+                mavenBundle().groupId("org.hibernate").artifactId("hibernate-validator").versionAsInProject(),
+                mavenBundle().groupId("org.jboss.logging").artifactId("jboss-logging").versionAsInProject(),
+                mavenBundle().groupId("com.fasterxml").artifactId("classmate").versionAsInProject(),
+                mavenBundle().groupId("javax.el").artifactId("javax.el-api").versionAsInProject()
+        ));
+
+        return Helper.asArray(options);
+    }
+
+    @Override
+    protected Feature getJsonProviderFeature() {
+        // Turn off BV otherwise the test is not stable.
+        return new Feature() {
+
+            @Override
+            public boolean configure(final FeatureContext context) {
+                context.register(new MoxyJsonConfig()
+                        .property(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE)
+                        .resolver());
+
+                return true;
+            }
+        };
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java
new file mode 100644
index 0000000..6ab4c56
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonProcessingTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * Basic test of Json Processing.
+ *
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public class JsonProcessingTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+
+        options.addAll(Helper.expandedList(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                // JSON processing.
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-json-processing")
+                        .versionAsInProject(),
+                mavenBundle().groupId("org.glassfish").artifactId("javax.json").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish").artifactId("jsonp-jaxrs").versionAsInProject()
+        ));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @POST
+        public JsonObject postJsonObject(final JsonObject jsonObject) {
+            return jsonObject;
+        }
+    }
+
+    @Test
+    public void testJsonObject() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(Resource.class);
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+        final JsonObject jsonObject = Json.createObjectBuilder().add("foo", "bar").build();
+
+        final Client client = ClientBuilder.newClient();
+        final JsonObject entity = client
+                .target(baseUri)
+                .request(MediaType.APPLICATION_JSON_TYPE)
+                .post(Entity.json(jsonObject), JsonObject.class);
+
+        System.out.println("RESULT = " + entity);
+        assertEquals(jsonObject, entity);
+
+        server.shutdownNow();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonResource.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonResource.java
new file mode 100644
index 0000000..f9c3bdd
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/JsonResource.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("json")
+public class JsonResource {
+
+    @XmlRootElement
+    public static class NameBean {
+
+        public String name = "Harrison";
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public NameBean getBean() {
+        NameBean result = new NameBean();
+        result.name = "Jim";
+
+        return result;
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/MultiPartTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/MultiPartTest.java
new file mode 100644
index 0000000..3b17ca0
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/MultiPartTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.media.multipart.BodyPart;
+import org.glassfish.jersey.media.multipart.BodyPartEntity;
+import org.glassfish.jersey.media.multipart.MultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public class MultiPartTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+
+        options.addAll(Helper.expandedList(
+                // jersey-multipart dependencies
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-multipart").versionAsInProject(),
+                mavenBundle().groupId("org.jvnet.mimepull").artifactId("mimepull").versionAsInProject()));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @Path("/multipart-simple")
+    public static class MultiPartResource {
+
+        @GET
+        @Produces("multipart/mixed")
+        public Response one() {
+            MultiPart entity = new MultiPart();
+            BodyPart part = new BodyPart("This is the only segment", new MediaType("text", "plain"));
+            entity.getBodyParts().add(part);
+            return Response.ok(entity).type("multipart/mixed").build();
+        }
+
+    }
+
+    @Test
+    public void testMultiPartResource() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(MultiPartResource.class).register(new MultiPartFeature());
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        Client c = ClientBuilder.newClient(new ClientConfig().register(MultiPartFeature.class));
+        final Response response = c.target(baseUri).path("/multipart-simple").request().buildGet().invoke();
+
+        MultiPart result = response.readEntity(MultiPart.class);
+        System.out.println("RESULT = " + result);
+
+        checkEntity("This is the only segment", (BodyPartEntity) result.getBodyParts().get(0).getEntity());
+
+        server.shutdownNow();
+    }
+
+    private void checkEntity(String expected, BodyPartEntity entity) throws IOException {
+        // Convert the raw bytes into a String
+        InputStreamReader sr = new InputStreamReader(entity.getInputStream());
+        StringWriter sw = new StringWriter();
+        while (true) {
+            int ch = sr.read();
+            if (ch < 0) {
+                break;
+            }
+            sw.append((char) ch);
+        }
+        // Perform the comparison
+        assertEquals(expected, sw.toString());
+    }
+
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
new file mode 100644
index 0000000..48480b2
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.grizzly2.servlet.GrizzlyWebContainerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletContainer;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * NOTE: This test is excluded on JDK6 as it requires Servlet 3.1 API that is built against JDK 7.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public class PackageScanningTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+
+        options.addAll(Helper.expandedList(
+                // vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"),
+
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-sse").versionAsInProject(),
+
+                mavenBundle().groupId("javax.servlet").artifactId("javax.servlet-api").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http-servlet").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-servlet-core")
+                        .versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-grizzly2-servlet")
+                        .versionAsInProject(),
+
+                // MBR/MBW for JSON-P is on the classpath.
+                mavenBundle().groupId("org.glassfish").artifactId("javax.json").versionAsInProject()
+        ));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @Test
+    public void testSimpleResource() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig().packages(SimpleResource.class.getPackage().getName());
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        _testScannedResources(server);
+    }
+
+    @Test
+    public void testSimpleResourceInitParameters() throws Exception {
+        Map<String, String> initParams = new HashMap<String, String>();
+        initParams.put(
+                ServerProperties.PROVIDER_PACKAGES,
+                SimpleResource.class.getPackage().getName());
+
+        // TODO - temporary workaround
+        // This is a workaround related to issue JERSEY-2093; grizzly (1.9.5) needs to have the correct context
+        // classloader set
+        ClassLoader myClassLoader = getClass().getClassLoader();
+        ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
+        HttpServer server = null;
+        try {
+            Thread.currentThread().setContextClassLoader(myClassLoader);
+            server = GrizzlyWebContainerFactory.create(baseUri, ServletContainer.class, initParams);
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalContextClassLoader);
+        }
+        // END of workaround - when grizzly updated to more recent version, only the inner line of try clause should remain:
+
+        _testScannedResources(server);
+    }
+
+    private void _testScannedResources(final HttpServer server) throws Exception {
+        final Client client = ClientBuilder.newClient();
+
+        assertEquals("OK", client.target(baseUri).path("/simple").request().get(String.class));
+        // resources in subpackages aren't supported yet because the osgi recursive scanning is set to false
+//        assertEquals("sub-OK", client.target(baseUri).path("/sub-packaged").request().get(String.class));
+
+        server.shutdownNow();
+    }
+
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/ResourceBundleTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/ResourceBundleTest.java
new file mode 100644
index 0000000..7f6c6fb
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/ResourceBundleTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2010, 2018 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.osgi.test.basic;
+
+import java.io.ByteArrayOutputStream;
+import java.net.URI;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import java.util.logging.SimpleFormatter;
+import java.util.logging.StreamHandler;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.UriBuilder;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.internal.Errors;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+
+/**
+ * Ensures server localization resource bundle gets loaded fine in OSGi runtime.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(PaxExam.class)
+public class ResourceBundleTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+        options.addAll(Helper.expandedList(
+                // PaxRunnerOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+        ));
+
+        return Helper.asArray(options);
+    }
+
+    @Path("/non-deployable")
+    public static class BadResource {
+
+        @GET
+        private String getMe() {
+            return "no way";
+        }
+    }
+
+    @Test
+    public void testBadResource() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(BadResource.class);
+
+        ByteArrayOutputStream logOutput = new ByteArrayOutputStream();
+        Handler logHandler = new StreamHandler(logOutput, new SimpleFormatter());
+
+        GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig, false);
+
+        // TODO: there should be a better way to get the log output!
+        final Enumeration<String> loggerNames = LogManager.getLogManager().getLoggerNames();
+        while (loggerNames.hasMoreElements()) {
+            String name = loggerNames.nextElement();
+            if (name.startsWith("org.glassfish")) {
+                LogManager.getLogManager().getLogger(Errors.class.getName()).addHandler(logHandler);
+            }
+        }
+        GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig, false);
+        logOutput.flush();
+        final String logOutputAsString = logOutput.toString();
+
+        Assert.assertFalse(logOutputAsString.contains("[failed to localize]"));
+        Assert.assertTrue(logOutputAsString.contains("BadResource"));
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/SimpleResource.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/SimpleResource.java
new file mode 100644
index 0000000..6b672cb
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/SimpleResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 2018 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.osgi.test.basic;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/simple")
+public class SimpleResource {
+
+    @GET
+    public String getMe() {
+        return "OK";
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/SseTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/SseTest.java
new file mode 100644
index 0000000..c558f32
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/SseTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2012, 2018 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.osgi.test.basic;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.inject.Inject;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.media.sse.EventOutput;
+import org.glassfish.jersey.media.sse.EventSource;
+import org.glassfish.jersey.media.sse.InboundEvent;
+import org.glassfish.jersey.media.sse.OutboundEvent;
+import org.glassfish.jersey.media.sse.SseFeature;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * Basic test for SSE module OSGification.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@RunWith(PaxExam.class)
+public class SseTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Inject
+    protected BundleContext bundleContext;
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+
+        options.addAll(Helper.expandedList(
+                // Jersey SSE dependencies
+                mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-sse").versionAsInProject()));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @Path("/sse")
+    public static class SseResource {
+
+        @GET
+        @Produces(SseFeature.SERVER_SENT_EVENTS)
+        public EventOutput getIt() throws IOException {
+            final EventOutput result = new EventOutput();
+            result.write(new OutboundEvent.Builder().name("event1").data(String.class, "ping").build());
+            result.write(new OutboundEvent.Builder().name("event2").data(String.class, "pong").build());
+            result.close();
+            return result;
+        }
+    }
+
+    @Test
+    public void testSse() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(SseResource.class, SseFeature.class);
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+        Client c = ClientBuilder.newClient();
+        c.register(SseFeature.class);
+
+        final List<String> data = new LinkedList<String>();
+        final CountDownLatch latch = new CountDownLatch(2);
+
+        final EventSource eventSource = new EventSource(c.target(baseUri).path("/sse")) {
+
+            @Override
+            public void onEvent(InboundEvent event) {
+                try {
+                    data.add(event.readData());
+                    latch.countDown();
+                } catch (ProcessingException e) {
+                    // ignore
+                }
+            }
+        };
+
+        assertTrue(latch.await(2, TimeUnit.SECONDS));
+
+        eventSource.close();
+        assertEquals(2, data.size());
+
+        server.shutdownNow();
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/WebResourceFactoryTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/WebResourceFactoryTest.java
new file mode 100644
index 0000000..3f99393
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/WebResourceFactoryTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2013, 2018 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.osgi.test.basic;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriBuilder;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.glassfish.jersey.client.proxy.WebResourceFactory;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.osgi.test.util.Helper;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import static org.junit.Assert.assertEquals;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * @author Michal Gajdos
+ */
+@RunWith(PaxExam.class)
+public class WebResourceFactoryTest {
+
+    private static final String CONTEXT = "/jersey";
+
+    private static final URI baseUri = UriBuilder
+            .fromUri("http://localhost")
+            .port(Helper.getPort())
+            .path(CONTEXT).build();
+
+    @Configuration
+    public static Option[] configuration() {
+        List<Option> options = Helper.getCommonOsgiOptions();
+
+        options.addAll(Helper.expandedList(
+                // jersey-multipart dependencies
+                mavenBundle().groupId("org.glassfish.jersey.ext").artifactId("jersey-proxy-client").versionAsInProject()));
+
+        options = Helper.addPaxExamMavenLocalRepositoryProperty(options);
+        return Helper.asArray(options);
+    }
+
+    @XmlRootElement
+    public static class MyBean {
+        public String name;
+    }
+
+    @Path("myresource")
+    public static interface MyResourceIfc {
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        String getIt();
+
+        @POST
+        @Consumes({MediaType.APPLICATION_XML})
+        @Produces({MediaType.APPLICATION_XML})
+        List<MyBean> postIt(List<MyBean> entity);
+
+        @Path("{id}")
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        String getId(@PathParam("id") String id);
+
+        @Path("query")
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        String getByName(@QueryParam("name") String name);
+
+        @Path("subresource")
+        MySubResourceIfc getSubResource();
+    }
+
+    public static class MyResource implements MyResourceIfc {
+
+        @Override
+        public String getIt() {
+            return "Got it!";
+        }
+
+        @Override
+        public List<MyBean> postIt(List<MyBean> entity) {
+            return entity;
+        }
+
+        @Override
+        public String getId(String id) {
+            return id;
+        }
+
+        @Override
+        public String getByName(String name) {
+            return name;
+        }
+
+        @Override
+        public MySubResourceIfc getSubResource() {
+            return new MySubResource();
+        }
+    }
+
+    public static class MySubResource implements MySubResourceIfc {
+
+        @Override
+        public MyBean getMyBean() {
+            MyBean bean = new MyBean();
+            bean.name = "Got it!";
+            return bean;
+        }
+    }
+
+    public static interface MySubResourceIfc {
+
+        @GET
+        @Produces(MediaType.APPLICATION_XML)
+        public MyBean getMyBean();
+    }
+
+    private HttpServer server;
+    private MyResourceIfc resource;
+
+    @Before
+    public void setUp() throws Exception {
+        final ResourceConfig resourceConfig = new ResourceConfig(MyResource.class);
+        server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+        resource = WebResourceFactory.newResource(MyResourceIfc.class, ClientBuilder.newClient().target(baseUri));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        server.shutdownNow();
+    }
+
+    @Test
+    public void testGetIt() {
+        assertEquals("Got it!", resource.getIt());
+    }
+
+    @Test
+    public void testPostIt() {
+        MyBean bean = new MyBean();
+        bean.name = "Foo";
+        assertEquals("Foo", resource.postIt(Collections.singletonList(bean)).get(0).name);
+    }
+
+    @Test
+    public void testPathParam() {
+        assertEquals("Bar", resource.getId("Bar"));
+    }
+
+    @Test
+    public void testQueryParam() {
+        assertEquals("Jersey2", resource.getByName("Jersey2"));
+    }
+
+    @Test
+    public void testSubResource() {
+        assertEquals("Got it!", resource.getSubResource().getMyBean().name);
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/subpackage/SimpleResourceSubpackaged.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/subpackage/SimpleResourceSubpackaged.java
new file mode 100644
index 0000000..e5434eb
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/subpackage/SimpleResourceSubpackaged.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2018 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.osgi.test.basic.subpackage;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+/**
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+@Path("/sub-packaged")
+public class SimpleResourceSubpackaged {
+    @GET
+    public String getMe() {
+        return "sub-OK";
+    }
+}
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java
new file mode 100644
index 0000000..c3d9670
--- /dev/null
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010, 2018 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.osgi.test.util;
+
+import java.security.AccessController;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.ops4j.pax.exam.Option;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackage;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+/**
+ * Helper class to be used by individual tests.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ * @author Michal Gajdos
+ */
+public class Helper {
+
+    /**
+     * Jersey HTTP port.
+     */
+    private static final int port = getEnvVariable(TestProperties.CONTAINER_PORT, 8080);
+
+    /**
+     * Returns an integer value of given system property, or a default value
+     * as defined by the other method parameter, if the system property can
+     * not be used.
+     *
+     * @param varName      name of the system variable.
+     * @param defaultValue the default value to return if the system variable is missing or can not be parsed as an integer.
+     * @return an integer value taken either from the system property or the default value as defined by the defaultValue parameter.
+     */
+    public static int getEnvVariable(final String varName, int defaultValue) {
+        if (null == varName) {
+            return defaultValue;
+        }
+        String varValue = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(varName));
+        if (null != varValue) {
+            try {
+                return Integer.parseInt(varValue);
+            } catch (NumberFormatException e) {
+                // will return default value below
+            }
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Returns a value of {@value TestProperties#CONTAINER_PORT} property which should be used as port number for test container.
+     *
+     * @return port number.
+     */
+    public static int getPort() {
+        return port;
+    }
+
+    /**
+     * Adds a system property for Maven local repository location to the PaxExam OSGi runtime if a "localRepository" property
+     * is present in the map of the system properties.
+     *
+     * @param options list of options to add the local repository property to.
+     * @return list of options enhanced by the local repository property if this property is set or the given list if the
+     *         previous condition is not met.
+     */
+    public static List<Option> addPaxExamMavenLocalRepositoryProperty(List<Option> options) {
+        final String localRepository = AccessController.doPrivileged(PropertiesHelper.getSystemProperty("localRepository"));
+
+        if (localRepository != null) {
+            options.addAll(expandedList(systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepository)));
+        }
+
+        return options;
+    }
+
+    /**
+     * Convert list of OSGi options to an array.
+     *
+     * @param options list of OSGi options.
+     * @return array of OSGi options.
+     */
+    public static Option[] asArray(final List<Option> options) {
+        return options.toArray(new Option[options.size()]);
+    }
+
+    /**
+     * Create new list of common OSGi integration test options.
+     *
+     * @return list of common OSGi integration test options.
+     */
+    public static List<Option> getCommonOsgiOptions() {
+        return getCommonOsgiOptions(true);
+    }
+
+    /**
+     * Create new list of common OSGi integration test options.
+     *
+     * @param includeJerseyJaxRsLibs indicates whether JaxRs and Jersey bundles should be added into the resulting list of
+     * options.
+     * @return list of common OSGi integration test options.
+     */
+    public static List<Option> getCommonOsgiOptions(final boolean includeJerseyJaxRsLibs) {
+        final List<Option> options = new LinkedList<Option>(expandedList(
+                // systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("FINEST"),
+                systemProperty("org.osgi.service.http.port").value(String.valueOf(port)),
+                systemProperty(TestProperties.CONTAINER_PORT).value(String.valueOf(port)),
+                systemProperty("org.osgi.framework.system.packages.extra").value("javax.annotation"),
+
+                // javax.annotation has to go first!
+                mavenBundle().groupId("javax.annotation").artifactId("javax.annotation-api").versionAsInProject(),
+
+                junitBundles(),
+
+                // HK2
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-api").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("osgi-resource-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-locator").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2").artifactId("hk2-utils").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("javax.inject").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.hk2.external").artifactId("aopalliance-repackaged").versionAsInProject(),
+                mavenBundle().groupId("org.javassist").artifactId("javassist").versionAsInProject(),
+
+                // Grizzly
+                systemPackage("sun.misc"),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-framework").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http").versionAsInProject(),
+                mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http-server").versionAsInProject(),
+
+                // javax.validation
+                mavenBundle().groupId("javax.validation").artifactId("validation-api").versionAsInProject(),
+
+                // Jersey Grizzly
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-grizzly2-http")
+                        .versionAsInProject()
+        ));
+
+        if (includeJerseyJaxRsLibs) {
+            options.addAll(expandedList(
+                    // JAX-RS API
+                    mavenBundle().groupId("javax.ws.rs").artifactId("javax.ws.rs-api").versionAsInProject(),
+
+                    // Jersey bundles
+                    mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-common").versionAsInProject(),
+                    mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-jaxb").versionAsInProject(),
+                    mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-server").versionAsInProject(),
+                    mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-client").versionAsInProject(),
+
+                    // Jersey Injection provider
+                    mavenBundle().groupId("org.glassfish.jersey.inject").artifactId("jersey-hk2").versionAsInProject()
+            ));
+        }
+
+        return addPaxExamMavenLocalRepositoryProperty(options);
+    }
+
+    /**
+     * Create expanded options list from the supplied options.
+     *
+     * @param options options to be expanded into the option list.
+     * @return expanded options list.
+     */
+    public static List<Option> expandedList(Option... options) {
+        return Arrays.asList(options(options));
+    }
+}
diff --git a/tests/osgi/functional/src/test/resources/log4j.properties b/tests/osgi/functional/src/test/resources/log4j.properties
new file mode 100644
index 0000000..13d56f8
--- /dev/null
+++ b/tests/osgi/functional/src/test/resources/log4j.properties
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2013, 2018 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
+#
+
+log4j.rootCategory=ERROR, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout.ConversionPattern=[%30.30c{1}] - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
diff --git a/tests/osgi/pom.xml b/tests/osgi/pom.xml
new file mode 100644
index 0000000..e55fc8a
--- /dev/null
+++ b/tests/osgi/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.osgi</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-osgi</name>
+
+    <description>
+        Test modules containing test scenario types that cover OSGi.
+    </description>
+
+    <modules>
+        <module>functional</module>
+    </modules>
+</project>
diff --git a/tests/performance/benchmarks/README.md b/tests/performance/benchmarks/README.md
new file mode 100644
index 0000000..6616896
--- /dev/null
+++ b/tests/performance/benchmarks/README.md
@@ -0,0 +1,29 @@
+[//]: # " Copyright (c) 2015, 2018 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 "
+
+## How to run?
+
+To run all benchmarks execute:
+
+`mvn clean install exec:exec` or `mvn clean install && java -jar target/benchmarks.jar`
+
+To run specific benchmark, e.g. `JacksonBenchmark`:
+
+`mvn clean install && java -cp target/benchmarks.jar org.glassfish.jersey.tests.performance.benchmark.JacksonBenchmark`
+
+## Where to find more info/examples?
+
+JMH page: http://openjdk.java.net/projects/code-tools/jmh/
+
+JMH examples: http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/
diff --git a/tests/performance/benchmarks/pom.xml b/tests/performance/benchmarks/pom.xml
new file mode 100644
index 0000000..40314a0
--- /dev/null
+++ b/tests/performance/benchmarks/pom.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>performance-test-benchmarks</artifactId>
+    <packaging>jar</packaging>
+    <name>performance-test-benchmarks</name>
+
+    <description>Micro-benchmarks to see performance of Jersey and it's features</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-grizzly-connector</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <executable>java</executable>
+                    <arguments>
+                        <argument>-classpath</argument>
+                        <classpath />
+                        <argument>org.glassfish.jersey.tests.performance.benchmark.AllBenchmarks</argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <configuration>
+                    <minimizeJar>false</minimizeJar>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>shade-archive</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <finalName>${uberjar.name}</finalName>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.glassfish.jersey.tests.performance.benchmark.AllBenchmarks</mainClass>
+                                </transformer>
+                            </transformers>
+                            <filters>
+                                <filter>
+                                    <!--
+                                        Shading signed JARs will fail without this.
+                                        http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
+                                    -->
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkMode>always</forkMode>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <uberjar.name>benchmark</uberjar.name>
+    </properties>
+</project>
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
new file mode 100644
index 0000000..badc3ee
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/AllBenchmarks.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark;
+
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+/**
+ * @author Michal Gajdos
+ */
+public class AllBenchmarks {
+
+    public static void main(final String[] args) throws Exception {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(ClientBenchmark.class.getSimpleName())
+                .include(JacksonBenchmark.class.getSimpleName())
+                .include(LocatorBenchmark.class.getSimpleName())
+                .include(JerseyUriBuilderBenchmark.class.getSimpleName())
+                // Measure throughput in seconds (ops/s).
+                .mode(Mode.Throughput)
+                .timeUnit(TimeUnit.SECONDS)
+                // Warm-up setup.
+                .warmupIterations(16)
+                .warmupTime(TimeValue.milliseconds(2500))
+                // Measurement setup.
+                .measurementIterations(16)
+                .measurementTime(TimeValue.milliseconds(2500))
+                // Fork! (Invoke benchmarks in separate JVM)
+                .forks(1)
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/ClientBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/ClientBenchmark.java
new file mode 100644
index 0000000..bf55df9
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/ClientBenchmark.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.util.client.LoopBackConnectorProvider;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * Locator {@link org.glassfish.jersey.server.ApplicationHandler} benchmark.
+ *
+ * @author Michal Gajdos
+ */
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 16, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 16, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class ClientBenchmark {
+
+    private volatile Client client;
+
+    @Setup
+    public void start() throws Exception {
+        client = ClientBuilder.newClient(LoopBackConnectorProvider.getClientConfig());
+    }
+
+    @TearDown
+    public void shutdown() {
+        client.close();
+    }
+
+    @Benchmark
+    public Response get() throws Exception {
+        return client.target("foo").request().get();
+    }
+
+    @Benchmark
+    public Response post() throws Exception {
+        return client.target("foo").request().post(Entity.text("bar"));
+    }
+
+    @Benchmark
+     public Response asyncBlock() throws Exception {
+        return client.target("foo").request().async().get().get();
+    }
+
+    @Benchmark
+    public Future<Response> asyncIgnore() throws Exception {
+        return client.target("foo").request().async().get(new InvocationCallback<Response>() {
+            @Override
+            public void completed(final Response response) {
+                // NOOP
+            }
+
+            @Override
+            public void failed(final Throwable throwable) {
+                // NOOP
+            }
+        });
+    }
+
+    @Benchmark
+    public Future<Response> asyncEntityIgnore() throws Exception {
+        return client.target("foo").request().async().post(Entity.text("bar"), new InvocationCallback<Response>() {
+            @Override
+            public void completed(final Response response) {
+                // NOOP
+            }
+
+            @Override
+            public void failed(final Throwable throwable) {
+                // NOOP
+            }
+        });
+    }
+
+    public static void main(final String[] args) throws Exception {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(ClientBenchmark.class.getSimpleName())
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/JacksonBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/JacksonBenchmark.java
new file mode 100644
index 0000000..8b527ff
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/JacksonBenchmark.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark;
+
+import java.util.concurrent.TimeUnit;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.test.util.server.ContainerRequestBuilder;
+import org.glassfish.jersey.tests.performance.benchmark.entity.json.JacksonApplication;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * Jackson Entity Filtering {@link org.glassfish.jersey.server.ApplicationHandler} benchmark.
+ *
+ * @author Michal Gajdos
+ */
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 16, time = 2500, timeUnit = TimeUnit.MILLISECONDS)
+@Measurement(iterations = 16, time = 2500, timeUnit = TimeUnit.MILLISECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class JacksonBenchmark {
+
+    @Param(value = {"projects/basic", "projects/detailed"})
+    private String path;
+
+    @Param(value = {"false", "true"})
+    private String filtering;
+
+    private volatile ApplicationHandler handler;
+    private volatile ContainerRequest request;
+
+    @Setup
+    public void start() throws Exception {
+        handler = new ApplicationHandler(new JacksonApplication(Boolean.valueOf(filtering)));
+    }
+
+    @Setup(Level.Iteration)
+    public void request() {
+        request = ContainerRequestBuilder
+                .from(path, "GET")
+                .build();
+    }
+
+    @TearDown
+    public void shutdown() {
+    }
+
+    @Benchmark
+    public ContainerResponse measureResource() throws Exception {
+        return handler.apply(request).get();
+    }
+
+    public static void main(final String[] args) throws Exception {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(JacksonBenchmark.class.getSimpleName())
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/JerseyUriBuilderBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/JerseyUriBuilderBenchmark.java
new file mode 100644
index 0000000..a02570c
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/JerseyUriBuilderBenchmark.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017, 2018 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.performance.benchmark;
+
+import java.util.concurrent.TimeUnit;
+
+import org.glassfish.jersey.uri.internal.JerseyUriBuilder;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * {@link JerseyUriBuilder} benchmark for parsing templates.
+ *
+ * @author David Schlosnagle
+ */
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 16, time = 2500, timeUnit = TimeUnit.MILLISECONDS)
+@Measurement(iterations = 16, time = 2500, timeUnit = TimeUnit.MILLISECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class JerseyUriBuilderBenchmark {
+
+    @Param(value = {"http://localhost:8080/a/b/c", "https://localhost:443/{a}/{b}/{c:.+}"})
+    private String uriTemplate;
+
+    private volatile JerseyUriBuilder uriBuilder;
+
+    @Setup
+    public void start() throws Exception {
+        uriBuilder = new JerseyUriBuilder();
+    }
+
+    @Benchmark
+    public JerseyUriBuilder uri() throws Exception {
+        return uriBuilder.uri(uriTemplate);
+    }
+
+    public static void main(final String[] args) throws Exception {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(JerseyUriBuilderBenchmark.class.getSimpleName())
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/LocatorBenchmark.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/LocatorBenchmark.java
new file mode 100644
index 0000000..e4c9da9
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/LocatorBenchmark.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.test.util.server.ContainerRequestBuilder;
+import org.glassfish.jersey.tests.performance.benchmark.server.LocatorApplication;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * Locator {@link org.glassfish.jersey.server.ApplicationHandler} benchmark.
+ *
+ * @author Michal Gajdos
+ */
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@Warmup(iterations = 16, time = 2500, timeUnit = TimeUnit.MILLISECONDS)
+@Measurement(iterations = 16, time = 2500, timeUnit = TimeUnit.MILLISECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class LocatorBenchmark {
+
+    @Param(value = {"helloworld", "helloworld/locator"})
+    private String path;
+
+    @Param(value = {"GET", "POST", "PUT"})
+    private String method;
+
+    private volatile ApplicationHandler handler;
+    private volatile ContainerRequest request;
+
+    @Setup
+    public void start() throws Exception {
+        handler = new ApplicationHandler(new LocatorApplication());
+    }
+
+    @Setup(Level.Iteration)
+    public void request() {
+        request = ContainerRequestBuilder
+                .from(path, method)
+                .entity("GET".equals(method) ? null : "Hello World!", handler)
+                .build();
+    }
+
+    @TearDown
+    public void shutdown() {
+    }
+
+    @Benchmark
+    public Future<ContainerResponse> measure() throws Exception {
+        return handler.apply(request);
+    }
+
+    public static void main(final String[] args) throws Exception {
+        final Options opt = new OptionsBuilder()
+                // Register our benchmarks.
+                .include(LocatorBenchmark.class.getSimpleName())
+                .build();
+
+        new Runner(opt).run();
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/JacksonApplication.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/JacksonApplication.java
new file mode 100644
index 0000000..22ce001
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/JacksonApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JacksonApplication extends ResourceConfig {
+
+    public JacksonApplication(final boolean entityFilteringEnabled) {
+        register(ProjectsResource.class);
+
+        if (entityFilteringEnabled) {
+            register(EntityFilteringFeature.class);
+        }
+        register(JacksonFeature.class);
+
+        // Turn off Monitoring to not affect benchmarks.
+        property(ServerProperties.MONITORING_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, false);
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/Project.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/Project.java
new file mode 100644
index 0000000..4e33d9a
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/Project.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Project entity class. Fields {@code tasks} and {@code users} are available only in detailed view (defined via
+ * {@link ProjectDetailedView}).
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"JavaDoc", "UnusedDeclaration"})
+@XmlRootElement
+public class Project {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    @ProjectDetailedView
+    private List<Task> tasks;
+
+    @ProjectDetailedView
+    private List<User> users;
+
+    public Project() {
+    }
+
+    public Project(final Long id, final String name, final String description) {
+        this.id = id;
+        this.name = name;
+        this.description = description;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public List<Task> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(final List<Task> tasks) {
+        this.tasks = tasks;
+    }
+
+    public List<User> getUsers() {
+        return users;
+    }
+
+    public void setUsers(final List<User> users) {
+        this.users = users;
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/ProjectDetailedView.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/ProjectDetailedView.java
new file mode 100644
index 0000000..c40edee
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/ProjectDetailedView.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Entity-filtering annotation used to define detailed view on returned {@link Project} entities.
+ *
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface ProjectDetailedView {
+
+    /**
+     * Factory class for creating instances of {@code ProjectDetailedView} annotation.
+     */
+    public static class Factory extends AnnotationLiteral<ProjectDetailedView> implements ProjectDetailedView {
+
+        private Factory() {
+        }
+
+        public static ProjectDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/ProjectsResource.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/ProjectsResource.java
new file mode 100644
index 0000000..2e50f4b
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/ProjectsResource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Resource class for {@link Project projects}. Provides methods to retrieve projects in "default" view ({@link #getProjects()}
+ * and in "detailed" view ({@link #getDetailedProjects()}.
+ *
+ * @author Michal Gajdos
+ */
+@Path("projects")
+@Produces("application/json")
+public class ProjectsResource {
+
+    private static final List<Project> projects;
+
+    static {
+        final Project project = new Project(1L, "foo", "bar");
+        final User user = new User(1L, "foo", "foo@bar.baz");
+        final Task task = new Task(1L, "foo", "bar");
+
+        project.setUsers(Arrays.asList(user));
+        project.setTasks(Arrays.asList(task, task));
+
+        projects = Arrays.asList(project, project);
+    }
+
+    @GET
+    @Path("basic")
+    public List<Project> getProjects() {
+        return getDetailedProjects();
+    }
+
+    @GET
+    @Path("detailed")
+    @ProjectDetailedView
+    public List<Project> getDetailedProjects() {
+        return projects;
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/Task.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/Task.java
new file mode 100644
index 0000000..91a2f5c
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/Task.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Task entity class. Fields {@code project} and {@code user} are available only in detailed view (defined via
+ * {@link TaskDetailedView}).
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+@XmlRootElement
+public class Task {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    @TaskDetailedView
+    private Project project;
+
+    @TaskDetailedView
+    private User user;
+
+    public Task() {
+    }
+
+    public Task(final Long id, final String name, final String description) {
+        this.id = id;
+        this.name = name;
+        this.description = description;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    public void setProject(final Project project) {
+        this.project = project;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(final User user) {
+        this.user = user;
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/TaskDetailedView.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/TaskDetailedView.java
new file mode 100644
index 0000000..1caf023
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/TaskDetailedView.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Entity-filtering annotation used to define detailed view on returned {@link Task} entities.
+ *
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface TaskDetailedView {
+
+    /**
+     * Factory class for creating instances of {@code TaskDetailedView} annotation.
+     */
+    public static class Factory extends AnnotationLiteral<TaskDetailedView> implements TaskDetailedView {
+
+        private Factory() {
+        }
+
+        public static TaskDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/User.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/User.java
new file mode 100644
index 0000000..3b52b22
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/User.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * User entity class. Fields {@code projects} and {@code tasks} are available only in detailed view (defined via
+ * {@link UserDetailedView} on getters).
+ *
+ * @author Michal Gajdos
+ */
+@SuppressWarnings({"UnusedDeclaration", "JavaDoc"})
+@XmlRootElement
+public class User {
+
+    private Long id;
+
+    private String name;
+
+    private String email;
+
+    private List<Project> projects;
+
+    private List<Task> tasks;
+
+    public User() {
+    }
+
+    public User(final Long id, final String name, final String email) {
+        this.id = id;
+        this.name = name;
+        this.email = email;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(final Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(final String email) {
+        this.email = email;
+    }
+
+    @UserDetailedView
+    public List<Project> getProjects() {
+        return projects;
+    }
+
+    public void setProjects(final List<Project> projects) {
+        this.projects = projects;
+    }
+
+    @UserDetailedView
+    public List<Task> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(final List<Task> tasks) {
+        this.tasks = tasks;
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/UserDetailedView.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/UserDetailedView.java
new file mode 100644
index 0000000..b73fe8f
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/entity/json/UserDetailedView.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.entity.json;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.glassfish.jersey.internal.inject.AnnotationLiteral;
+import org.glassfish.jersey.message.filtering.EntityFiltering;
+
+/**
+ * Entity-filtering annotation used to define detailed view on returned {@link User} entities.
+ *
+ * @author Michal Gajdos
+ */
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EntityFiltering
+public @interface UserDetailedView {
+
+    /**
+     * Factory class for creating instances of {@code UserDetailedView} annotation.
+     */
+    public static class Factory extends AnnotationLiteral<UserDetailedView> implements UserDetailedView {
+
+        private Factory() {
+        }
+
+        public static UserDetailedView get() {
+            return new Factory();
+        }
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorApplication.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorApplication.java
new file mode 100644
index 0000000..4924cb7
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.server;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+
+/**
+ * @author Michal Gajdos
+ */
+public class LocatorApplication extends ResourceConfig {
+
+    public LocatorApplication() {
+        register(LocatorResource.class);
+
+        // Turn off Monitoring to not affect benchmarks.
+        property(ServerProperties.MONITORING_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_ENABLED, false);
+        property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, false);
+    }
+}
diff --git a/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorResource.java b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorResource.java
new file mode 100644
index 0000000..24228a9
--- /dev/null
+++ b/tests/performance/benchmarks/src/main/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.server;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("resource")
+@Produces("text/plain")
+public class LocatorResource {
+
+    @GET
+    public String get() {
+        return "Hello World!";
+    }
+
+    @POST
+    public String post(final String entity) {
+        return entity;
+    }
+
+    @PUT
+    public void put(final String entity) {
+        // NOOP
+    }
+
+    @Path("locator")
+    public Class<?> locator() {
+        return LocatorResource.class;
+    }
+}
diff --git a/tests/performance/benchmarks/src/test/java/org/glassfish/jersey/tests/performance/benchmark/server/JacksonTest.java b/tests/performance/benchmarks/src/test/java/org/glassfish/jersey/tests/performance/benchmark/server/JacksonTest.java
new file mode 100644
index 0000000..b611d34
--- /dev/null
+++ b/tests/performance/benchmarks/src/test/java/org/glassfish/jersey/tests/performance/benchmark/server/JacksonTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.server;
+
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.tests.performance.benchmark.entity.json.JacksonApplication;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class JacksonTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new JacksonApplication(false);
+    }
+
+    @Test
+    public void testResourceMethod() {
+        final Response response = target().path("projects/basic").request().get();
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+    }
+
+    @Test
+    public void testSubResourceMethod() {
+        final Response response = target().path("projects/detailed").request().get();
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+    }
+}
diff --git a/tests/performance/benchmarks/src/test/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorTest.java b/tests/performance/benchmarks/src/test/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorTest.java
new file mode 100644
index 0000000..ea70157
--- /dev/null
+++ b/tests/performance/benchmarks/src/test/java/org/glassfish/jersey/tests/performance/benchmark/server/LocatorTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.benchmark.server;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+
+import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Michal Gajdos
+ */
+public class LocatorTest extends JerseyTest {
+
+    @Override
+    protected ResourceConfig configure() {
+        enable(TestProperties.LOG_TRAFFIC);
+
+        return new LocatorApplication();
+    }
+
+    @Test
+    public void testResourceMethod() {
+        final Response response = target().path("resource").request("text/plain").get();
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is("Hello World!"));
+    }
+
+    @Test
+    public void testSubResourceMethod() {
+        final Response response = target().path("resource").path("locator").request("text/plain").get();
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is("Hello World!"));
+    }
+
+    @Test
+    public void testPost() {
+        final Response response = target().path("resource")
+                .request("text/plain")
+                .post(Entity.text("Hello World!"));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is("Hello World!"));
+    }
+
+    @Test
+    public void testPostLocator() {
+        final Response response = target().path("resource").path("locator")
+                .request("text/plain")
+                .post(Entity.text("Hello World!"));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(200));
+        assertThat("Unexpected response entity.", response.readEntity(String.class), is("Hello World!"));
+    }
+
+    @Test
+    public void testPut() {
+        final Response response = target().path("resource")
+                .request("text/plain")
+                .put(Entity.text("Hello World!"));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(204));
+    }
+
+    @Test
+    public void testPutLocator() {
+        final Response response = target().path("resource").path("locator")
+                .request("text/plain")
+                .put(Entity.text("Hello World!"));
+
+        assertThat("Wrong HTTP response code returned.", response.getStatus(), is(204));
+    }
+}
diff --git a/tests/performance/etc/client/json-post.lua b/tests/performance/etc/client/json-post.lua
new file mode 100644
index 0000000..1db504c
--- /dev/null
+++ b/tests/performance/etc/client/json-post.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "POST"
+io.input("json.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/json"
diff --git a/tests/performance/etc/client/json-put.lua b/tests/performance/etc/client/json-put.lua
new file mode 100644
index 0000000..a32d686
--- /dev/null
+++ b/tests/performance/etc/client/json-put.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "PUT"
+io.input("json.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/json"
diff --git a/tests/performance/etc/client/json.dat b/tests/performance/etc/client/json.dat
new file mode 100644
index 0000000..521c930
--- /dev/null
+++ b/tests/performance/etc/client/json.dat
@@ -0,0 +1 @@
+{"name":"Wolfgang","age":21,"address":"Salzburg"}
diff --git a/tests/performance/etc/client/kryo-post.lua b/tests/performance/etc/client/kryo-post.lua
new file mode 100644
index 0000000..77c4fa1
--- /dev/null
+++ b/tests/performance/etc/client/kryo-post.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "POST"
+io.input("kryo.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/x-kryo"
diff --git a/tests/performance/etc/client/kryo-put.lua b/tests/performance/etc/client/kryo-put.lua
new file mode 100644
index 0000000..39d96fa
--- /dev/null
+++ b/tests/performance/etc/client/kryo-put.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "PUT"
+io.input("kryo.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/x-kryo"
diff --git a/tests/performance/etc/client/kryo.dat b/tests/performance/etc/client/kryo.dat
new file mode 100644
index 0000000..2045bc3
--- /dev/null
+++ b/tests/performance/etc/client/kryo.dat
@@ -0,0 +1 @@
+ParióJuleó
\ No newline at end of file
diff --git a/tests/performance/etc/client/person-post.lua b/tests/performance/etc/client/person-post.lua
new file mode 100644
index 0000000..0256635
--- /dev/null
+++ b/tests/performance/etc/client/person-post.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "POST"
+io.input("person.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/person"
diff --git a/tests/performance/etc/client/person-put.lua b/tests/performance/etc/client/person-put.lua
new file mode 100644
index 0000000..9bfc4bb
--- /dev/null
+++ b/tests/performance/etc/client/person-put.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "PUT"
+io.input("person.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/person"
diff --git a/tests/performance/etc/client/person.dat b/tests/performance/etc/client/person.dat
new file mode 100644
index 0000000..fd0955b
--- /dev/null
+++ b/tests/performance/etc/client/person.dat
@@ -0,0 +1,4 @@
+name: Mozart
+age: 21
+address: Salzburg
+
diff --git a/tests/performance/etc/client/text-post.lua b/tests/performance/etc/client/text-post.lua
new file mode 100644
index 0000000..c4b6fa9
--- /dev/null
+++ b/tests/performance/etc/client/text-post.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "POST"
+io.input("text.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "text/plain"
diff --git a/tests/performance/etc/client/text-put.lua b/tests/performance/etc/client/text-put.lua
new file mode 100644
index 0000000..3f91001
--- /dev/null
+++ b/tests/performance/etc/client/text-put.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "PUT"
+io.input("text.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "text/plain"
diff --git a/tests/performance/etc/client/text.dat b/tests/performance/etc/client/text.dat
new file mode 100644
index 0000000..c743cac
--- /dev/null
+++ b/tests/performance/etc/client/text.dat
@@ -0,0 +1 @@
+Prague ( /ˈprɑːɡ/; Czech: Praha pronounced [ˈpraɦa] ( listen)) is the capital and largest city of the Czech Republic. It is the fourteenth-largest city in the European Union.[5] It is also the historical capital of Bohemia proper. Situated in the north-west of the country on the Vltava river, the city is home to about 1.3 million people, while its larger urban zone is estimated to have a population of nearly 2 million.[3] The city has a temperate oceanic climate, with warm summers and chilly winters.
diff --git a/tests/performance/etc/client/xml-post.lua b/tests/performance/etc/client/xml-post.lua
new file mode 100644
index 0000000..f365ee9
--- /dev/null
+++ b/tests/performance/etc/client/xml-post.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "POST"
+io.input("xml.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/xml"
diff --git a/tests/performance/etc/client/xml-put.lua b/tests/performance/etc/client/xml-put.lua
new file mode 100644
index 0000000..7aa11c9
--- /dev/null
+++ b/tests/performance/etc/client/xml-put.lua
@@ -0,0 +1,21 @@
+--[[
+
+    Copyright (c) 2015, 2018 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
+
+--]]
+wrk.method = "PUT"
+io.input("xml.dat")
+wrk.body = io.read("*all")
+wrk.headers["Content-Type"] = "application/xml"
diff --git a/tests/performance/etc/client/xml.dat b/tests/performance/etc/client/xml.dat
new file mode 100644
index 0000000..22624a1
--- /dev/null
+++ b/tests/performance/etc/client/xml.dat
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><name>Wolfgang</name><age>21</age><address>Salzburg</address></person>
diff --git a/tests/performance/etc/data/MEASUREMENT_DATA b/tests/performance/etc/data/MEASUREMENT_DATA
new file mode 100644
index 0000000..e728638
--- /dev/null
+++ b/tests/performance/etc/data/MEASUREMENT_DATA
@@ -0,0 +1,79 @@
+#
+# Copyright (c) 2015, 2018 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
+#
+
+#
+# File format:
+# application directory name|command line to generate load on client machines|JMX URI for the application|MBean name|output filename
+#
+
+mbw-text-plain|wrk -c64 -t16 -d800s  http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.text.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-text-get.properties
+mbw-text-plain|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.text.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-text-put.properties
+mbw-text-plain|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.text.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-text-post.properties
+
+mbw-json-moxy|wrk -c64 -t16 -d800s  http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-json-moxy-get.properties
+mbw-json-moxy|wrk -c64 -t16 -d800s -s json-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-json-moxy-put.properties
+mbw-json-moxy|wrk -c64 -t16 -d800s -s json-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-json-moxy-post.properties
+
+mbw-json-jackson|wrk -c64 -t16 -d800s  http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-json-jackson-get.properties
+mbw-json-jackson|wrk -c64 -t16 -d800s -s json-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-json-jackson-put.properties
+mbw-json-jackson|wrk -c64 -t16 -d800s -s json-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-json-jackson-post.properties
+
+mbw-kryo|wrk -c64 -t16 -d800s  http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.kryo.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-kryo-get.properties
+mbw-kryo|wrk -c64 -t16 -d800s -s kryo-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.kryo.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-kryo-put.properties
+mbw-kryo|wrk -c64 -t16 -d800s -s kryo-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.kryo.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-kryo-post.properties
+
+mbw-xml-moxy|wrk -c64 -t16 -d800s  http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-xml-moxy-get.properties
+mbw-xml-moxy|wrk -c64 -t16 -d800s -s xml-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-xml-moxy-post.properties
+mbw-xml-moxy|wrk -c64 -t16 -d800s -s xml-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-xml-moxy-put.properties
+
+mbw-xml-jaxb|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-xml-jaxb-get.properties
+mbw-xml-jaxb|wrk -c64 -t16 -d800s -s xml-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-xml-jaxb-post.properties
+mbw-xml-jaxb|wrk -c64 -t16 -d800s -s xml-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-xml-jaxb-put.properties
+
+mbw-custom-provider|wrk -c64 -t16 -d800s -s person-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.custom.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-custom-post.properties
+mbw-custom-provider|wrk -c64 -t16 -d800s -s person-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.custom.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-custom-put.properties
+mbw-custom-provider|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.mbw.custom.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|mbw-custom-get.properties
+
+param-srl|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/srl/jednaBedna\;m=bedna\?q=jedna|org.glassfish.jersey.tests.performance.param.srl.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|param-srl-get.properties
+param-srl|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/srm/jednaBedna\;m=bedna\?q=jedna|org.glassfish.jersey.tests.performance.param.srl.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|param-srm-get.properties
+
+filter-global|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.global.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-global-get.properties
+filter-global|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.global.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-global-put.properties
+filter-global|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.global.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-global-post.properties
+
+filter-name|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.name.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-name-post.properties
+filter-name|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.name.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-name-put.properties
+filter-name|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.name.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-name-get.properties
+
+filter-dynamic|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.dynamic.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-dynamic-post.properties
+filter-dynamic|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.dynamic.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-dynamic-put.properties
+filter-dynamic|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.filter.dynamic.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|filter-dynamic-get.properties
+
+interceptor-global|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.global.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-global-post.properties
+interceptor-global|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.global.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-global-put.properties
+interceptor-global|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.global.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-global-get.properties
+
+interceptor-name|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.name.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-name-post.properties
+interceptor-name|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.name.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-name-put.properties
+interceptor-name|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.name.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-name-get.properties
+
+interceptor-dynamic|wrk -c64 -t16 -d800s -s text-post.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.dynamic.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-dynamic-post.properties
+interceptor-dynamic|wrk -c64 -t16 -d800s -s text-put.lua http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.dynamic.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-dynamic-put.properties
+interceptor-dynamic|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/|org.glassfish.jersey.tests.performance.interceptor.dynamic.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|interceptor-dynamic-get.properties
+
+proxy-injection|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/method-injected/without-parameters|org.glassfish.jersey.tests.performance.proxy.injection.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|proxy-injection-raw-gets.properties
+proxy-injection|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/method-injected/all-parameters|org.glassfish.jersey.tests.performance.proxy.injection.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|proxy-injection-method-params.properties
+proxy-injection|wrk -c64 -t16 -d800s http://SERVER_MACHINE:SERVER_PORT/APP_CONTEXT/field-injected/without-parameters|org.glassfish.jersey.tests.performance.proxy.injection.JaxRsApplication||metrics:name=org.glassfish.jersey.server.ApplicationHandler.handle|proxy-injection-field-params.properties
diff --git a/tests/performance/etc/hudson/jersey-performance-test-common.sh b/tests/performance/etc/hudson/jersey-performance-test-common.sh
new file mode 100644
index 0000000..da4f8c1
--- /dev/null
+++ b/tests/performance/etc/hudson/jersey-performance-test-common.sh
@@ -0,0 +1,264 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+APP_LIST=(mbw-text-plain \
+          mbw-json-moxy mbw-json-jackson \
+          mbw-xml-moxy mbw-xml-jaxb \
+          mbw-custom-provider \
+          mbw-kryo \
+          param-srl \
+          filter-global filter-name filter-dynamic \
+          interceptor-global interceptor-name interceptor-dynamic \
+          proxy-injection)
+
+WARM_UP_SECONDS=300
+WAIT_FOR_APP_STARTUP_SEC=5
+WAIT_FOR_APP_RUNNING_SEC=60
+CHECK_RUNNER_INTERVAL=5
+CHECK_TERM_INTERVAL=10
+
+JMX_URI_TEMPLATE="service:jmx:rmi:///jndi/rmi://SERVER_MACHINE:11112/jmxrmi"
+SAMPLES=30
+
+#uncomment for debug purposes:
+#WARM_UP_SECONDS=3
+#WAIT_FOR_APP_STARTUP_SEC=2
+#WAIT_FOR_APP_RUNNING_SEC=1
+#CHECK_RUNNER_INTERVAL=1
+#CHECK_TERM_INTERVAL=2
+#SAMPLES=3
+#/debug
+
+MODULES_TO_BUILD=""
+for app in ${APP_LIST[*]}; do
+  MODULES_TO_BUILD="$MODULES_TO_BUILD,tests/performance/test-cases/$app"
+done
+MODULES_TO_BUILD=`echo $MODULES_TO_BUILD|sed -e's/,//'`
+
+SERVER_LIST=()
+CLIENT_LIST_ALL=()
+
+
+function waitForGroupStatus() {
+  echo "########### Waiting for group status: $*"
+  RUNNER_ID=$1
+  shift
+  GROUP_ID=$1
+  shift
+  STATUS=$1
+
+  echo "#`date`" > $STATUS_DIR/.runner.$RUNNER_ID.waiting.$GROUP_ID.$STATUS
+  FILE="$STATUS_DIR/.group.$GROUP_ID.running.$RUNNER_ID.$STATUS"
+
+  rm -f $STATUS_DIR/.group.$GROUP_ID.running.$RUNNER_ID.*
+
+  available=false
+  while [ "$available" != true ]; do
+    if [ -e "$FILE" ]; then
+      available=true
+    else
+      sleep 1
+    fi
+  done
+}
+
+function releaseRunnerAndGroup() {
+  echo "########### Release Runner and Group: $*"
+  RUNNER_ID=$1
+  shift
+  GROUP_ID=$1
+
+  echo "#`date`" > $STATUS_DIR/.runner.$RUNNER_ID.available
+  rm -f $STATUS_DIR/.group.$GROUP_ID.running.$RUNNER_ID.* $STATUS_DIR/.runner.$RUNNER_ID.running
+}
+
+function checkWaitingRunners() {
+  echo "########### Check Waiting Runners"
+  for waiting_file in `ls $STATUS_DIR/.runner.*.waiting.*.lock 2> /dev/null`; do
+    filename_array=(`basename $waiting_file | tr "." " "`)
+
+    runner_id=${filename_array[1]}
+    group_id=${filename_array[3]}
+    status=${filename_array[4]}
+
+    _files=($STATUS_DIR/.group.$group_id.running.*)
+    if [ ! -f "${_files}" ]; then
+      echo "#`date`" > "$STATUS_DIR/.group.$group_id.running.$runner_id.lock"
+      rm $waiting_file
+    fi
+  done
+
+  for waiting_file in `ls $STATUS_DIR/.runner.*.waiting.*.open 2> /dev/null`; do
+    filename_array=(`basename $waiting_file | tr "." " "`)
+
+    runner_id=${filename_array[1]}
+    group_id=${filename_array[3]}
+    status=${filename_array[4]}
+
+    _files=($STATUS_DIR/.group.$group_id.running.*.lock)
+    if [ ! -f "${_files}" ]; then
+      echo "#`date`" > "$STATUS_DIR/.group.$group_id.running.$runner_id.open"
+      rm $waiting_file
+    fi
+  done
+}
+
+function createMachineFiles {
+  echo "########### Creating machine files in $STATUS_DIR for: $*"
+  RUNNER_ID=$1
+  shift
+  GROUP_ID=$1
+  shift
+  SERVER_MACHINE=$1
+  shift
+  CLIENT_LIST=($@)
+
+  echo ${GROUP_ID} > $STATUS_DIR/.runner.$RUNNER_ID.group
+  echo "#`date`" > $STATUS_DIR/.runner.$RUNNER_ID.available
+  echo ${SERVER_MACHINE} > $STATUS_DIR/.runner.$RUNNER_ID.server
+  echo ${CLIENT_LIST[@]} > $STATUS_DIR/.runner.$RUNNER_ID.clients
+
+  SERVER_LIST=("${SERVER_LIST[@]}" "$SERVER_MACHINE")
+  CLIENT_LIST_ALL=("${CLIENT_LIST_ALL[@]}" "${CLIENT_LIST[@]}")
+}
+
+function waitForTerminator {
+  echo "########### Waiting for finish"
+  # wait for the last round to finish
+  terminated=false
+  while [ "$terminated" != true ]; do
+    checkWaitingRunners
+    _files=($STATUS_DIR/.runner.*.running)
+    if [ ! -f "${_files}" ]; then
+      terminated=true
+    fi
+    if [ "$terminated" != true ]; then
+      echo "########### Terminated tests: $terminated, waiting $CHECK_TERM_INTERVAL sec..."
+      sleep $CHECK_TERM_INTERVAL
+    fi
+  done
+
+  echo "DONE!"
+
+  wait
+  sleep 4
+  wait
+}
+
+function testLoop {
+  # Following is the main measurement loop
+  # MEASUREMENT_DATA is the input data in the following format:
+  # application directory name|command line to generate load on client machines|JMX URI for the application|MBean name|output filename
+
+  echo "########### Let's test it, reading from $MEASUREMENT_DATA file"
+
+  TOTAL_COUNT=`cat $MEASUREMENT_DATA | grep -v "^#" | grep "^." | wc -l | sed -e 's/ *//g'`
+  INDEX=0
+  cat $MEASUREMENT_DATA | grep -v "^#" | grep "^." | while IFS="\|" read app ab_cmdline app_class agent_param mbean filename
+  do
+    INDEX=`expr $INDEX + 1`
+    echo "========================================= DATA [$INDEX/$TOTAL_COUNT] =============================================="
+    echo "app       = $app"
+    echo "app_class = $app_class"
+    echo "ab_cmdline= $ab_cmdline"
+
+    spawned=false
+    while [ "$spawned" != true ]; do
+      for runner_file in `ls $STATUS_DIR/.runner.*.available 2> /dev/null`; do
+        if [ "$spawned" != true ]; then
+          filename_array=(`basename $runner_file | tr "." " "`)
+          actual_runner=${filename_array[1]}
+          echo "#`date`" > $STATUS_DIR/.runner.$actual_runner.running
+          rm $runner_file
+
+          SERVER_MACHINE=`cat $STATUS_DIR/.runner.$actual_runner.server`
+          APP_CONTEXT=$app
+          CLIENT_LIST=(`cat $STATUS_DIR/.runner.$actual_runner.clients`)
+          ab_cmdline=`echo $ab_cmdline | sed -e"s/SERVER_MACHINE/$SERVER_MACHINE/" | sed -e"s/SERVER_PORT/$SERVER_PORT/" | sed -e"s/APP_CONTEXT/$APP_CONTEXT/"`
+          JMX_URI=`echo $JMX_URI_TEMPLATE | sed -e"s/SERVER_MACHINE/$SERVER_MACHINE/"`
+          group_id=`cat $STATUS_DIR/.runner.$actual_runner.group`
+
+          echo "#[$INDEX/$TOTAL_COUNT]" >> $STATUS_DIR/.runner.$actual_runner.running
+          echo "$app" >> $STATUS_DIR/.runner.$actual_runner.running
+          echo "$app_class" >> $STATUS_DIR/.runner.$actual_runner.running
+          echo "$ab_cmdline" >> $STATUS_DIR/.runner.$actual_runner.running
+          echo "$filename" >> $STATUS_DIR/.runner.$actual_runner.running
+          echo "$mbean" >> $STATUS_DIR/.runner.$actual_runner.running
+          echo "$agent_param" >> $STATUS_DIR/.runner.$actual_runner.running
+
+          spawned=true
+          singleTest &
+        fi
+      done
+      checkWaitingRunners
+      if [ "$spawned" != true ]; then
+        sleep $CHECK_RUNNER_INTERVAL
+      fi
+    done
+  done
+}
+
+function removeOldCapturedData {
+  rm -f $WORKSPACE/*.properties
+}
+
+function retrieveJmxClient {
+  echo "########### Retrieving JMX client"
+  scp jerseyrobot@${SERVER_LIST[0]}:jmxclient.jar .
+}
+
+function prepareClients {
+  echo "########### Copy client files to each client"
+  for CLIENT_MACHINE in ${CLIENT_LIST_ALL[@]}; do
+    echo "... copy client files to ${CLIENT_MACHINE}"
+    scp $WORKSPACE/jersey/tests/performance/etc/client/* jerseyrobot@${CLIENT_MACHINE}:. &
+  done
+
+  wait
+}
+
+function buildTestAppOnServers {
+  echo "########### Building test applications on each server"
+  # git fetch jersey on the server machine and build all apps there:
+  for SERVER_MACHINE in ${SERVER_LIST[@]}; do
+    ssh -n jerseyrobot@${SERVER_MACHINE} '(cd $HOME/workspace/jersey && '$GIT_FETCH_COMMAND' && mvn -pl '$MODULES_TO_BUILD' -am -Dskip.tests=true clean install)' &
+  done
+
+  wait
+}
+
+function cleanupHudsonSlave {
+  rm -f $STATUS_DIR/.runner.* $STATUS_DIR/.group.*
+}
+
+function cleanupServer {
+  echo "########### Kill java processes on server $1"
+  ssh -n jerseyrobot@$1 'if ! test -e `ps h o pid -Cjava`; then kill -s TERM `ps h o pid -Cjava` ; fi'
+}
+
+function cleanupServers {
+  echo "########### Kill all java processes on each server"
+  for SERVER_MACHINE in ${SERVER_LIST[@]}; do
+    cleanupServer ${SERVER_MACHINE}
+  done
+}
+
+
+trap "cleanupHudsonSlave; cleanupServers" EXIT SIGTERM SIGINT
+
+#uncomment for debug purposes:
+#trap 'echo "[$BASH_SOURCE:$LINENO] $BASH_COMMAND" >> .debug; tail -10 .debug > .debug.swap; mv .debug.swap .debug' DEBUG
diff --git a/tests/performance/etc/hudson/jersey-performance-test-grizzly.sh b/tests/performance/etc/hudson/jersey-performance-test-grizzly.sh
new file mode 100644
index 0000000..2cf2a22
--- /dev/null
+++ b/tests/performance/etc/hudson/jersey-performance-test-grizzly.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+#
+# Environment setup:
+#
+# GIT_FETCH_COMMAND - specify GIT command to get sources to test, e.g. git checkout master && git pull
+#
+# STATUS_DIR=$HOME/.hudson-jersey3-performance-test
+# mkdir -p $STATUS_DIR
+# source jersey-performance-test-common.sh
+# test machine sets: cfg#, group, server, clients
+# createMachineFiles 1 1 server1 client1a client1b
+# createMachineFiles 1 2 server2 client2a client2b
+# MEASUREMENT_DATA=~/MEASUREMENT_DATA
+#
+
+SERVER_PORT=8080
+
+function singleTest() {
+  echo "================================================================================="
+  echo "===== SINGLE TEST RUN, SERVER=$SERVER_MACHINE, loader=$ab_cmdline, app=$app, JMX_URI=$JMX_URI, group_id=$group_id ====="
+  echo "================================================================================="
+
+  echo "########### going to start the test app $app"
+  ssh -n jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/runners/jersey-grizzly-runner; rm -rf app; mkdir -p app; cp $HOME/workspace/jersey/tests/performance/test-cases/'$app'/target/runner/lib/* app/; JAVA_OPTIONS="-javaagent:$HOME/jersey-perftest-agent.jar" ./start.sh '$app_class' http://0.0.0.0:8080/'$app'/ 4 8)' &
+
+  waitForGroupStatus $actual_runner $group_id "open"
+
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo "########### going to start load generator at $client_machine"
+    (sleep $WAIT_FOR_APP_STARTUP_SEC; ssh -n jerseyrobot@$client_machine "nohup $ab_cmdline" & ) &
+  done
+
+  echo "########### waiting $WARM_UP_SECONDS sec to warm up server"
+  sleep $WARM_UP_SECONDS
+
+  echo "########### warm up finished, terminating ab clients..."
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo -n "########### warm up finished, going to stop load generator at $client_machine..."
+    ssh -n jerseyrobot@$client_machine 'if ! test -e `ps h o pid -Cwrk`; then kill -s INT `ps h o pid -Cwrk` ; fi'
+    echo " done."
+  done
+
+  waitForGroupStatus $actual_runner $group_id "lock"
+
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo "########### going to start load generator at $client_machine again"
+    (ssh -n jerseyrobot@$client_machine "nohup $ab_cmdline" & ) &
+  done
+
+  echo "########### waiting before start capturing jmx data"
+  sleep $WAIT_FOR_APP_RUNNING_SEC
+
+  echo "########### starting jmx client to capture data"
+  if ! java -cp jmxclient.jar org.glassfish.jersey.tests.performance.jmxclient.Main $JMX_URI "$mbean" OneMinuteRate $SAMPLES $filename; then
+    echo "########### ERROR WHEN PROCESSING LINE#${LINE_NUMBER}, test-case: ${app}, mbean: ${mbean}, filename: ${filename}!"
+  fi
+
+  echo "########### jmx client finished, terminating ab clients..."
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo -n "########### going to stop load generator at $client_machine..."
+    ssh -n jerseyrobot@$client_machine 'if ! test -e `ps h o pid -Cwrk`; then kill -s INT `ps h o pid -Cwrk` ; fi'
+    echo " done."
+  done
+
+  echo "########### terminating test app..."
+  ssh jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/runners/jersey-grizzly-runner && ./stop.sh)'
+
+  cleanupServer $SERVER_MACHINE
+
+  releaseRunnerAndGroup $actual_runner $group_id
+}
+
+#
+# test process start
+#
+
+removeOldCapturedData
+
+retrieveJmxClient
+
+prepareClients
+
+buildTestAppOnServers
+
+echo "########### Package (lib dir) all test case applications"
+for SERVER_MACHINE in ${SERVER_LIST[@]}; do
+  for app in ${APP_LIST[*]}; do
+    ssh jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/test-cases/'$app'; mkdir -p target/runner/lib; cd target/runner/lib; unzip ../../*.zip)'
+  done
+done
+
+echo "########### Build and package (lib dir) test runner"
+for SERVER_MACHINE in ${SERVER_LIST[@]}; do
+  ssh -n jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey; mvn -pl tests/performance/runners/jersey-grizzly-runner clean install; cd tests/performance/runners/jersey-grizzly-runner; mkdir -p lib; rm -f lib/*.jar; cd lib; unzip ../target/*.zip)' &
+done
+
+wait
+
+cleanupServers
+
+testLoop
+
+waitForTerminator
diff --git a/tests/performance/etc/hudson/jersey-performance-test-wls.sh b/tests/performance/etc/hudson/jersey-performance-test-wls.sh
new file mode 100644
index 0000000..1b76227
--- /dev/null
+++ b/tests/performance/etc/hudson/jersey-performance-test-wls.sh
@@ -0,0 +1,139 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+#
+# Environment setup:
+#
+# GIT_FETCH_COMMAND - specify GIT command to get sources to test, e.g. git checkout master && git pull
+# WEBLOGIC_OUI_URL - URL to download WLS installation jar from
+#
+# STATUS_DIR=$HOME/.hudson-jersey3-performance-test
+# mkdir -p $STATUS_DIR
+# source jersey-performance-test-common.sh
+# test machine sets: cfg#, group, server, clients
+# createMachineFiles 1 1 server1 client1a client1b
+# createMachineFiles 1 2 server2 client2a client2b
+# MEASUREMENT_DATA=~/MEASUREMENT_DATA
+#
+
+LOGS_DIR=$WORKSPACE/logs
+
+SERVER_PORT=7001
+
+function singleTest() {
+  echo "================================================================================="
+  echo "===== SINGLE TEST RUN, SERVER=$SERVER_MACHINE, loader=$ab_cmdline, app=$app, JMX_URI=$JMX_URI, group_id=$group_id ====="
+  echo "================================================================================="
+
+  echo "########### Start WLS server"
+  ssh -n jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/runners/jersey-wls-runner; ./start.sh)' &
+
+  echo "########### waiting for WLS server start"
+  while [ ! `wget -q --server-response --no-proxy http://${SERVER_MACHINE}:7001 2>&1 | awk '/^  HTTP/{print $2}'` ]; do
+    sleep 5
+    echo "# ${SERVER_MACHINE}"
+  done
+
+  echo "########### going to deploy/start the test app $app"
+  ssh -n jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/runners/jersey-wls-runner; ./deploy.sh $PWD/../../test-cases/'$app'/target/runner/'$app'.war)'
+
+  waitForGroupStatus $actual_runner $group_id "open"
+
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo "########### going to start load generator at $client_machine"
+    (sleep $WAIT_FOR_APP_STARTUP_SEC; ssh -n jerseyrobot@$client_machine "nohup $ab_cmdline" & ) &
+  done
+
+  echo "########### waiting $WARM_UP_SECONDS sec to warm up server"
+  sleep $WARM_UP_SECONDS
+
+  echo "########### warm up finished, terminating ab clients..."
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo -n "########### warm up finished, going to stop load generator at $client_machine..."
+    ssh -n jerseyrobot@$client_machine 'if ! test -e `ps h o pid -Cwrk`; then kill -s INT `ps h o pid -Cwrk` ; fi'
+    echo " done."
+  done
+
+  waitForGroupStatus $actual_runner $group_id "lock"
+
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo "########### going to start load generator at $client_machine again"
+    (ssh -n jerseyrobot@$client_machine "nohup $ab_cmdline" & ) &
+  done
+
+  echo "########### waiting before start capturing jmx data"
+  sleep $WAIT_FOR_APP_RUNNING_SEC
+
+  echo "########### starting jmx client to capture data"
+  if ! java -cp jmxclient.jar org.glassfish.jersey.tests.performance.jmxclient.Main $JMX_URI "$mbean" OneMinuteRate $SAMPLES $filename; then
+    echo "########### ERROR WHEN PROCESSING LINE#${LINE_NUMBER}, test-case: ${app}, mbean: ${mbean}, filename: ${filename}!"
+  fi
+
+  echo "########### jmx client finished, terminating ab clients..."
+  for client_machine in ${CLIENT_LIST[*]}; do
+    echo -n "########### going to stop load generator at $client_machine..."
+    ssh -n jerseyrobot@$client_machine 'if ! test -e `ps h o pid -Cwrk`; then kill -s INT `ps h o pid -Cwrk` ; fi'
+    echo " done."
+  done
+
+  echo "########### terminating test app..."
+  ssh jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/runners/jersey-wls-runner && ./stop.sh)'
+
+  echo "########### copy server and domain logs to hudson slave..."
+  just_filename=`echo ${filename} | sed -e 's/\.[^.]*$//'`
+  scp jerseyrobot@${SERVER_MACHINE}:workspace/jersey/tests/performance/runners/jersey-wls-runner/target/server.log $LOGS_DIR/${just_filename}-server.log
+  scp jerseyrobot@${SERVER_MACHINE}:workspace/jersey/tests/performance/runners/jersey-wls-runner/target/domain.log $LOGS_DIR/${just_filename}-domain.log
+
+  cleanupServer $SERVER_MACHINE
+
+  releaseRunnerAndGroup $actual_runner $group_id
+}
+
+#
+# test process start
+#
+
+mkdir -p $LOGS_DIR
+rm -f $LOGS_DIR/*
+
+removeOldCapturedData
+
+retrieveJmxClient
+
+prepareClients
+
+buildTestAppOnServers
+
+echo "########### Prepare war files of all test case applications"
+for SERVER_MACHINE in ${SERVER_LIST[@]}; do
+  for app in ${APP_LIST[*]}; do
+    ssh jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/test-cases/'$app'; mkdir -p target/runner; cp target/*.war target/runner/'$app'.war)'
+  done
+done
+
+echo "########### Install WLS server"
+for SERVER_MACHINE in ${SERVER_LIST[@]}; do
+  ssh -n jerseyrobot@${SERVER_MACHINE} '(cd workspace/jersey/tests/performance/runners/jersey-wls-runner; ./install.sh '$WEBLOGIC_OUI_URL')' &
+done
+
+wait
+
+cleanupServers
+
+testLoop
+
+waitForTerminator
diff --git a/tests/performance/jmx-client/pom.xml b/tests/performance/jmx-client/pom.xml
new file mode 100644
index 0000000..f497879
--- /dev/null
+++ b/tests/performance/jmx-client/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2010, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.glassfish.jersey.test.performance</groupId>
+  <artifactId>jmxclient</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>JmxClient</name>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+</project>
diff --git a/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java b/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java
new file mode 100644
index 0000000..9cfafb4
--- /dev/null
+++ b/tests/performance/jmx-client/src/main/java/org/glassfish/jersey/tests/performance/jmxclient/Main.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.jmxclient;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+/**
+ * JMX Client entry point.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Main {
+
+    public static void main(String[] args) throws Exception {
+
+//      e.g. "service:jmx:rmi:///jndi/rmi://sysifos.cz.oracle.com:11112/jmxrmi"
+        final String jmxUrl = args[0];
+//      e.g. "org.glassfish.jersey.test.performance.interceptor.dynamic:type=DynamicallyBoundInterceptorResource,name=gets"
+        final String mBeanName = args[1];
+//      e.g. "OneMinuteRate"
+        final String mBeanAttrName = args[2];
+//      e.g. 50
+        final int sampleCount = Integer.parseInt(args[3]);
+//      e.g. "phishing.properties"
+        final String propertiesFile = args[4];
+
+        System.out.printf("JMX URL = %s\nMBean = %s\nattribute = %s\nsamples = %d\nfilename = %s\n"
+                + "Going to connect...\n",
+                jmxUrl, mBeanName, mBeanAttrName, sampleCount, propertiesFile);
+
+        final JMXServiceURL url = new JMXServiceURL(jmxUrl);
+        final JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
+        final MBeanServerConnection mBeanServer = jmxc.getMBeanServerConnection();
+        final ObjectName mBeanObjectName = new ObjectName(mBeanName);
+
+        System.out.println("Connected...");
+
+        double totalSum = 0;
+        int samplesTaken = 0;
+
+        for (int i = 0; i < sampleCount; i++) {
+            Thread.sleep(5000);
+
+            Double sample = (Double) mBeanServer.getAttribute(mBeanObjectName, mBeanAttrName);
+
+            System.out.printf("OMR[%d]=%f\n", i, sample);
+
+            totalSum += sample;
+            samplesTaken++;
+        }
+
+        jmxc.close();
+
+        final double result = totalSum / samplesTaken;
+        writeResult(result, propertiesFile);
+
+        System.out.printf("\nAverage=%f\n", result);
+    }
+
+    private static void writeResult(double resultValue, String propertiesFile) throws IOException {
+        Properties resultProps = new Properties();
+        resultProps.put("YVALUE", Double.toString(resultValue));
+        resultProps.store(new FileOutputStream(propertiesFile), null);
+    }
+}
diff --git a/tests/performance/pom.xml b/tests/performance/pom.xml
new file mode 100644
index 0000000..6434cf2
--- /dev/null
+++ b/tests/performance/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.performance</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-performance</name>
+
+    <description>
+        Performance tests.
+    </description>
+
+    <modules>
+        <module>benchmarks</module>
+        <module>runners</module>
+        <module>test-cases</module>
+        <module>tools</module>
+    </modules>
+
+</project>
diff --git a/tests/performance/runners/jersey-grizzly-runner/pom.xml b/tests/performance/runners/jersey-grizzly-runner/pom.xml
new file mode 100644
index 0000000..4718865
--- /dev/null
+++ b/tests/performance/runners/jersey-grizzly-runner/pom.xml
@@ -0,0 +1,94 @@
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.runners</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+
+    <artifactId>jersey-grizzly-runner</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-grizzly-runner</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <archive>
+                        <index>true</index>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <mainClass>org.glassfish.jersey.tests.performance.runners.JerseyGrizzlyRunner</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.tests.performance.runners.JerseyGrizzlyRunner</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/assembly/zip-with-jars.xml</descriptor>
+                    </descriptors>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <!-- this is used for inheritance merges -->
+                        <phase>package</phase>
+                        <!-- append to the packaging phase. -->
+                        <goals>
+                            <goal>attached</goal>
+                            <!-- goals == mojos -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/runners/jersey-grizzly-runner/src/main/assembly/zip-with-jars.xml b/tests/performance/runners/jersey-grizzly-runner/src/main/assembly/zip-with-jars.xml
new file mode 100644
index 0000000..92a2a73
--- /dev/null
+++ b/tests/performance/runners/jersey-grizzly-runner/src/main/assembly/zip-with-jars.xml
@@ -0,0 +1,35 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+    <id>zip-with-jars</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <dependencySets>
+        <dependencySet>
+            <outputDirectory>/</outputDirectory>
+            <useProjectArtifact>true</useProjectArtifact>
+            <unpack>false</unpack>
+            <scope>runtime</scope>
+        </dependencySet>
+    </dependencySets>
+</assembly>
diff --git a/tests/performance/runners/jersey-grizzly-runner/src/main/java/org/glassfish/jersey/tests/performance/runners/JerseyGrizzlyRunner.java b/tests/performance/runners/jersey-grizzly-runner/src/main/java/org/glassfish/jersey/tests/performance/runners/JerseyGrizzlyRunner.java
new file mode 100644
index 0000000..bc651cb
--- /dev/null
+++ b/tests/performance/runners/jersey-grizzly-runner/src/main/java/org/glassfish/jersey/tests/performance/runners/JerseyGrizzlyRunner.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.runners;
+
+import java.net.URI;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
+import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
+
+/**
+ * Application class to start performance test web service at http://localhost:8080/ if the base URI
+ * is not passed via the second command line argument.
+ */
+public class JerseyGrizzlyRunner {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+
+    private static final int DEFAULT_SELECTORS = 4;
+    private static final int DEFAULT_WORKERS = 8;
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Jersey performance test web service application");
+
+        final String jaxRsApp = args.length > 0 ? args[0] : null;
+        //noinspection unchecked
+        final ResourceConfig resourceConfig = ResourceConfig
+                .forApplicationClass((Class<? extends Application>) Class.forName(jaxRsApp));
+        URI baseUri = args.length > 1 ? URI.create(args[1]) : BASE_URI;
+        int selectors = args.length > 2 ? Integer.parseInt(args[2]) : DEFAULT_SELECTORS;
+        int workers = args.length > 3 ? Integer.parseInt(args[3]) : DEFAULT_WORKERS;
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig, false);
+        final TCPNIOTransport transport = server.getListener("grizzly").getTransport();
+        transport.setSelectorRunnersCount(selectors);
+        transport.setWorkerThreadPoolConfig(ThreadPoolConfig.defaultConfig().setCorePoolSize(workers).setMaxPoolSize(workers));
+
+        server.start();
+
+        System.out.println(String.format("Application started.\nTry out %s\nHit Ctrl-C to stop it...",
+                baseUri));
+
+        while (server.isStarted()) {
+            Thread.sleep(600000);
+        }
+    }
+}
diff --git a/tests/performance/runners/jersey-grizzly-runner/start.sh b/tests/performance/runners/jersey-grizzly-runner/start.sh
new file mode 100644
index 0000000..76d45e4
--- /dev/null
+++ b/tests/performance/runners/jersey-grizzly-runner/start.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Copyright (c) 2012, 2018 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
+#
+
+LIBS=$(for l in `ls lib`; do echo -n lib/$l":";done)
+LIBS=`echo $LIBS | sed -es'/:$//'`
+
+APP=$(for l in `ls app`; do echo -n app/$l":";done)
+APP=`echo $APP | sed -es'/:$//'`
+
+nohup java -server -Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m \
+      -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods \
+      -cp $APP:$LIBS \
+      -Djava.net.preferIPv4Stack=true \
+      -Dcom.sun.management.jmxremote \
+      -Dcom.sun.management.jmxremote.port=11112 \
+      -Dcom.sun.management.jmxremote.authenticate=false \
+      -Dcom.sun.management.jmxremote.ssl=false \
+      -Dcom.sun.management.jmxremote.local.only=false \
+      $JAVA_OPTIONS \
+      org.glassfish.jersey.tests.performance.runners.JerseyGrizzlyRunner $* &
+echo $! > grizzly.pid
+wait
diff --git a/tests/performance/runners/jersey-grizzly-runner/stop.sh b/tests/performance/runners/jersey-grizzly-runner/stop.sh
new file mode 100644
index 0000000..08ea49c
--- /dev/null
+++ b/tests/performance/runners/jersey-grizzly-runner/stop.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+if test -f grizzly.pid; then
+  kill -9 `cat grizzly.pid`
+  rm grizzly.pid
+fi
+
diff --git a/tests/performance/runners/jersey-wls-runner/deploy.sh b/tests/performance/runners/jersey-wls-runner/deploy.sh
new file mode 100644
index 0000000..261030b
--- /dev/null
+++ b/tests/performance/runners/jersey-wls-runner/deploy.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+FILE=$1
+
+TARGET=$PWD/target
+TEST_DOMAIN=`cat $TARGET/test_domain.txt`
+
+. $TEST_DOMAIN/bin/setDomainEnv.sh
+
+java weblogic.Deployer -username weblogic -password weblogic1 -deploy $FILE
diff --git a/tests/performance/runners/jersey-wls-runner/install.sh b/tests/performance/runners/jersey-wls-runner/install.sh
new file mode 100644
index 0000000..529efd8
--- /dev/null
+++ b/tests/performance/runners/jersey-wls-runner/install.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+WEBLOGIC_URL=$1
+
+TARGET=$PWD/target
+OPT=$TARGET/opt
+
+rm -rf $TARGET
+mkdir -p $OPT
+
+cd $TARGET
+rm -f wls.jar
+wget --no-proxy $WEBLOGIC_URL -O wls.jar
+
+cd $OPT
+java -jar $TARGET/wls.jar
+cd wls*
+
+MW_HOME=`pwd`
+echo $MW_HOME > $TARGET/mw_home.txt
+
+. $MW_HOME/wlserver/server/bin/setWLSEnv.sh
+
+TEST_DOMAIN=$MW_HOME/hudson_test_domain
+
+mkdir -p $TEST_DOMAIN
+cd $TEST_DOMAIN
+
diff --git a/tests/performance/runners/jersey-wls-runner/start.sh b/tests/performance/runners/jersey-wls-runner/start.sh
new file mode 100644
index 0000000..a08f7ef
--- /dev/null
+++ b/tests/performance/runners/jersey-wls-runner/start.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+TARGET=$PWD/target
+MW_HOME=`cat $TARGET/mw_home.txt`
+TEST_DOMAIN=$MW_HOME/hudson_test_domain
+DOMAIN_NAME=HudsonTestDomain
+SERVER_NAME=HudsonTestServer
+PID_FILE=$TARGET/wls.pid
+
+echo $TEST_DOMAIN > $TARGET/test_domain.txt
+
+cd $MW_HOME
+. $MW_HOME/wlserver/server/bin/setWLSEnv.sh
+
+rm -rf $TEST_DOMAIN
+mkdir -p $TEST_DOMAIN
+cd $TEST_DOMAIN
+
+rm -f $TARGET/autodeploy
+ln -s $TEST_DOMAIN/autodeploy $TARGET/autodeploy
+
+rm -f $TARGET/server.log
+rm -f $TARGET/domain.log
+ln -s $TEST_DOMAIN/servers/$SERVER_NAME/logs/$SERVER_NAME.log $TARGET/server.log
+ln -s $TEST_DOMAIN/servers/$SERVER_NAME/logs/$DOMAIN_NAME.log $TARGET/domain.log
+
+JAVA_OPTIONS="-javaagent:$HOME/jersey-perftest-agent.jar"
+
+yes | nohup java -server \
+      -Xms1024m \
+      -Xmx1024m \
+      -XX:MaxPermSize=256m \
+      -Dweblogic.Domain=$DOMAIN_NAME \
+      -Dweblogic.Name=$SERVER_NAME \
+      -Dweblogic.management.username=weblogic \
+      -Dweblogic.management.password=weblogic1 \
+      -Dweblogic.ListenPort=7001 \
+      -Djava.security.egd=file:/dev/./urandom \
+      -Djava.net.preferIPv4Stack=true \
+      -Dcom.sun.management.jmxremote \
+      -Dcom.sun.management.jmxremote.port=11112 \
+      -Dcom.sun.management.jmxremote.authenticate=false \
+      -Dcom.sun.management.jmxremote.ssl=false \
+      -Dcom.sun.management.jmxremote.local.only=false \
+      $JAVA_OPTIONS \
+      weblogic.Server &
+
+echo $! > $PID_FILE
+
+# wait for server to start
+echo "******** WAITING FOR SERVER TO START"
+while [ ! `wget -q --server-response --no-proxy http://localhost:7001 2>&1 | awk '/^  HTTP/{print $2}'` ]; do
+  sleep 5
+  echo "*"
+done
+echo "******** SERVER IS READY"
diff --git a/tests/performance/runners/jersey-wls-runner/stop.sh b/tests/performance/runners/jersey-wls-runner/stop.sh
new file mode 100644
index 0000000..5b7691c
--- /dev/null
+++ b/tests/performance/runners/jersey-wls-runner/stop.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (c) 2015, 2018 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
+#
+
+TARGET=$PWD/target
+PID_FILE=$TARGET/wls.pid
+
+if test -f $PID_FILE; then
+    kill -9 `cat $PID_FILE`
+    rm $PID_FILE
+fi
diff --git a/tests/performance/runners/pom.xml b/tests/performance/runners/pom.xml
new file mode 100644
index 0000000..d0dc4d0
--- /dev/null
+++ b/tests/performance/runners/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.performance.runners</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-performance-runners</name>
+
+    <description>
+        Performance test runners.
+    </description>
+
+    <modules>
+        <module>jersey-grizzly-runner</module>
+    </modules>
+</project>
diff --git a/tests/performance/test-cases/assemblies/pom.xml b/tests/performance/test-cases/assemblies/pom.xml
new file mode 100644
index 0000000..cdd2c46
--- /dev/null
+++ b/tests/performance/test-cases/assemblies/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>assemblies</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-assemblies</name>
+    <description>Jersey performance test-cases shared assembly types.</description>
+
+</project>
diff --git a/tests/performance/test-cases/assemblies/src/main/resources/assemblies/war.xml b/tests/performance/test-cases/assemblies/src/main/resources/assemblies/war.xml
new file mode 100644
index 0000000..37cc628
--- /dev/null
+++ b/tests/performance/test-cases/assemblies/src/main/resources/assemblies/war.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+    <id>war</id>
+    <formats>
+        <format>war</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <dependencySets>
+        <dependencySet>
+            <outputDirectory>/WEB-INF/lib</outputDirectory>
+            <useProjectArtifact>true</useProjectArtifact>
+            <unpack>false</unpack>
+            <scope>runtime</scope>
+        </dependencySet>
+    </dependencySets>
+    <fileSets>
+        <fileSet>
+            <directory>src/main/webapp</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/tests/performance/test-cases/assemblies/src/main/resources/assemblies/zip-with-jars.xml b/tests/performance/test-cases/assemblies/src/main/resources/assemblies/zip-with-jars.xml
new file mode 100644
index 0000000..e65f8f7
--- /dev/null
+++ b/tests/performance/test-cases/assemblies/src/main/resources/assemblies/zip-with-jars.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+        xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+    <id>zip-with-jars</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <dependencySets>
+        <dependencySet>
+            <outputDirectory>/</outputDirectory>
+            <useProjectArtifact>true</useProjectArtifact>
+            <unpack>false</unpack>
+            <scope>runtime</scope>
+        </dependencySet>
+    </dependencySets>
+</assembly>
diff --git a/tests/performance/test-cases/filter-dynamic/pom.xml b/tests/performance/test-cases/filter-dynamic/pom.xml
new file mode 100644
index 0000000..43208e4
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>filter-dynamic</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-filter-dynamic-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicFilterFeature.java b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicFilterFeature.java
new file mode 100644
index 0000000..6539149
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicFilterFeature.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.dynamic;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+
+/**
+ * Dynamic feature to register custom filter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class DynamicFilterFeature implements DynamicFeature {
+
+    @Override
+    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+        if (DynamicallyBoundFilterResource.class == resourceInfo.getResourceClass()) {
+            context.register(DynamicallyBoundFilter.class);
+        }
+    }
+}
diff --git a/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicallyBoundFilter.java b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicallyBoundFilter.java
new file mode 100644
index 0000000..c3da591
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicallyBoundFilter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.dynamic;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Custom filter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class DynamicallyBoundFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        if (requestContext.hasEntity()) {
+            requestContext.setEntityStream(new SequenceInputStream(new ByteArrayInputStream("DYN_MATCH_IN".getBytes()),
+                    requestContext.getEntityStream()));
+        }
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        if (responseContext.hasEntity()) {
+            responseContext.setEntity("" + responseContext.getEntity() + "DYN_MATCH_OUT", null, MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+}
diff --git a/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicallyBoundFilterResource.java b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicallyBoundFilterResource.java
new file mode 100644
index 0000000..0a647c8
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/DynamicallyBoundFilterResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.dynamic;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Filtered resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+public class DynamicallyBoundFilterResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/JaxRsApplication.java b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/JaxRsApplication.java
new file mode 100644
index 0000000..0e68c16
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/src/main/java/org/glassfish/jersey/tests/performance/filter/dynamic/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.filter.dynamic;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(DynamicallyBoundFilterResource.class);
+         add(DynamicFilterFeature.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/filter-dynamic/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/filter-dynamic/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..11c53fe
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.filter.dynamic.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/filter-dynamic/src/test/java/org/glassfish/jersey/tests/performance/filter/dynamic/FilterTest.java b/tests/performance/test-cases/filter-dynamic/src/test/java/org/glassfish/jersey/tests/performance/filter/dynamic/FilterTest.java
new file mode 100644
index 0000000..7a31093
--- /dev/null
+++ b/tests/performance/test-cases/filter-dynamic/src/test/java/org/glassfish/jersey/tests/performance/filter/dynamic/FilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.filter.dynamic;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for intercepted text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class FilterTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("textDYN_MATCH_OUT", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals("DYN_MATCH_IN" + original + "DYN_MATCH_OUT", postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/filter-global/pom.xml b/tests/performance/test-cases/filter-global/pom.xml
new file mode 100644
index 0000000..21c00ea
--- /dev/null
+++ b/tests/performance/test-cases/filter-global/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>filter-global</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-filter-global-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/GlobalFilter.java b/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/GlobalFilter.java
new file mode 100644
index 0000000..5544482
--- /dev/null
+++ b/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/GlobalFilter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.global;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Global custom filter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class GlobalFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        if (requestContext.hasEntity()) {
+            requestContext.setEntityStream(new SequenceInputStream(new ByteArrayInputStream("PRE_MATCH_IN".getBytes()),
+                    requestContext.getEntityStream()));
+        }
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        if (responseContext.hasEntity()) {
+            responseContext.setEntity("" + responseContext.getEntity() + "PRE_MATCH_OUT", null, MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+}
diff --git a/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/JaxRsApplication.java b/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/JaxRsApplication.java
new file mode 100644
index 0000000..11eb07d
--- /dev/null
+++ b/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.filter.global;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(TextEntityResource.class);
+         add(GlobalFilter.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/TextEntityResource.java b/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/TextEntityResource.java
new file mode 100644
index 0000000..de1ce75
--- /dev/null
+++ b/tests/performance/test-cases/filter-global/src/main/java/org/glassfish/jersey/tests/performance/filter/global/TextEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.global;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+public class TextEntityResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/filter-global/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/filter-global/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..772373b
--- /dev/null
+++ b/tests/performance/test-cases/filter-global/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.filter.global.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/filter-global/src/test/java/org/glassfish/jersey/tests/performance/filter/global/FilterTest.java b/tests/performance/test-cases/filter-global/src/test/java/org/glassfish/jersey/tests/performance/filter/global/FilterTest.java
new file mode 100644
index 0000000..d1fe5ad
--- /dev/null
+++ b/tests/performance/test-cases/filter-global/src/test/java/org/glassfish/jersey/tests/performance/filter/global/FilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.filter.global;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for intercepted text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class FilterTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("textPRE_MATCH_OUT", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals("PRE_MATCH_IN" + original + "PRE_MATCH_OUT", postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/filter-name/pom.xml b/tests/performance/test-cases/filter-name/pom.xml
new file mode 100644
index 0000000..227739a
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>filter-name</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-filter-name-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/Filtered.java b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/Filtered.java
new file mode 100644
index 0000000..8928b61
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/Filtered.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.name;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.NameBinding;
+
+/**
+ * Custom binding annotation.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@NameBinding
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Filtered {}
diff --git a/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/JaxRsApplication.java b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/JaxRsApplication.java
new file mode 100644
index 0000000..4d1ecc7
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.filter.name;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(NameBoundFilterResource.class);
+         add(NameBoundFilter.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/NameBoundFilter.java b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/NameBoundFilter.java
new file mode 100644
index 0000000..7704c6f
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/NameBoundFilter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.name;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Custom filter.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@Filtered
+public class NameBoundFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        if (requestContext.hasEntity()) {
+            requestContext.setEntityStream(new SequenceInputStream(new ByteArrayInputStream("NAM_MATCH_IN".getBytes()),
+                    requestContext.getEntityStream()));
+        }
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+        if (responseContext.hasEntity()) {
+            responseContext.setEntity("" + responseContext.getEntity() + "NAM_MATCH_OUT", null, MediaType.TEXT_PLAIN_TYPE);
+        }
+    }
+}
diff --git a/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/NameBoundFilterResource.java b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/NameBoundFilterResource.java
new file mode 100644
index 0000000..98c49af
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/src/main/java/org/glassfish/jersey/tests/performance/filter/name/NameBoundFilterResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.filter.name;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Filtered resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+@Filtered
+public class NameBoundFilterResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/filter-name/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/filter-name/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..fbe4121
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.filter.name.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/filter-name/src/test/java/org/glassfish/jersey/tests/performance/filter/name/FilterTest.java b/tests/performance/test-cases/filter-name/src/test/java/org/glassfish/jersey/tests/performance/filter/name/FilterTest.java
new file mode 100644
index 0000000..776d0af
--- /dev/null
+++ b/tests/performance/test-cases/filter-name/src/test/java/org/glassfish/jersey/tests/performance/filter/name/FilterTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.filter.name;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for intercepted text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class FilterTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("textNAM_MATCH_OUT", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals("NAM_MATCH_IN" + original + "NAM_MATCH_OUT", postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-dynamic/pom.xml b/tests/performance/test-cases/interceptor-dynamic/pom.xml
new file mode 100644
index 0000000..e962e03
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>interceptor-dynamic</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-interceptor-dynamic-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicInterceptorFeature.java b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicInterceptorFeature.java
new file mode 100644
index 0000000..1903b09
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicInterceptorFeature.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.dynamic;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Custom feature to register interceptor.
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class DynamicInterceptorFeature implements DynamicFeature {
+
+    @Override
+    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+        if (DynamicallyBoundInterceptorResource.class == resourceInfo.getResourceClass()) {
+            context.register(DynamicallyBoundInterceptor.class);
+        }
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicallyBoundInterceptor.java b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicallyBoundInterceptor.java
new file mode 100644
index 0000000..0ce113e
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicallyBoundInterceptor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.dynamic;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * Custom interceptor.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class DynamicallyBoundInterceptor implements ReaderInterceptor, WriterInterceptor {
+
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+        context.setInputStream(new SequenceInputStream(new ByteArrayInputStream("READ ".getBytes()), context.getInputStream()));
+        return context.proceed();
+    }
+
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+    context.getOutputStream().write("WRITE ".getBytes());
+        context.proceed();
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicallyBoundInterceptorResource.java b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicallyBoundInterceptorResource.java
new file mode 100644
index 0000000..a99d426
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/DynamicallyBoundInterceptorResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.dynamic;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Intercepted resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+public class DynamicallyBoundInterceptorResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/JaxRsApplication.java b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/JaxRsApplication.java
new file mode 100644
index 0000000..f01d2ab
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/src/main/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.interceptor.dynamic;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(DynamicallyBoundInterceptorResource.class);
+         add(DynamicInterceptorFeature.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-dynamic/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/interceptor-dynamic/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..633ed97
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.interceptor.dynamic.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/interceptor-dynamic/src/test/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/InterceptorTest.java b/tests/performance/test-cases/interceptor-dynamic/src/test/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/InterceptorTest.java
new file mode 100644
index 0000000..e93174c
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-dynamic/src/test/java/org/glassfish/jersey/tests/performance/interceptor/dynamic/InterceptorTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.interceptor.dynamic;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for intercepted text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InterceptorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("WRITE text", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals("WRITE READ " + original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-global/pom.xml b/tests/performance/test-cases/interceptor-global/pom.xml
new file mode 100644
index 0000000..264dd02
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-global/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>interceptor-global</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-interceptor-global-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/CustomInterceptor.java b/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/CustomInterceptor.java
new file mode 100644
index 0000000..32d1bb1
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/CustomInterceptor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.global;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * Custom interceptor.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+public class CustomInterceptor implements ReaderInterceptor, WriterInterceptor {
+
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+        context.setInputStream(new SequenceInputStream(new ByteArrayInputStream("READ ".getBytes()), context.getInputStream()));
+        return context.proceed();
+    }
+
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+    context.getOutputStream().write("WRITE ".getBytes());
+        context.proceed();
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/JaxRsApplication.java b/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/JaxRsApplication.java
new file mode 100644
index 0000000..21d7308
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.interceptor.global;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(TextEntityResource.class);
+         add(CustomInterceptor.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/TextEntityResource.java b/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/TextEntityResource.java
new file mode 100644
index 0000000..dab5710
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-global/src/main/java/org/glassfish/jersey/tests/performance/interceptor/global/TextEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.global;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+public class TextEntityResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-global/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/interceptor-global/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..fe64786
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-global/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.interceptor.global.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/interceptor-global/src/test/java/org/glassfish/jersey/tests/performance/interceptor/global/InterceptorTest.java b/tests/performance/test-cases/interceptor-global/src/test/java/org/glassfish/jersey/tests/performance/interceptor/global/InterceptorTest.java
new file mode 100644
index 0000000..6f00764
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-global/src/test/java/org/glassfish/jersey/tests/performance/interceptor/global/InterceptorTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.interceptor.global;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for intercepted text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InterceptorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("WRITE text", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals("WRITE READ " + original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-name/pom.xml b/tests/performance/test-cases/interceptor-name/pom.xml
new file mode 100644
index 0000000..b08fecf
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>interceptor-name</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-interceptor-name-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/Intercepted.java b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/Intercepted.java
new file mode 100644
index 0000000..1185a0f
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/Intercepted.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.name;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+import javax.ws.rs.NameBinding;
+
+
+/**
+ * Custom annotation to bind interceptor.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@NameBinding
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Intercepted {}
diff --git a/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/InterceptedByNameResource.java b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/InterceptedByNameResource.java
new file mode 100644
index 0000000..d5fc36d
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/InterceptedByNameResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.name;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+@Intercepted
+public class InterceptedByNameResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/JaxRsApplication.java b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/JaxRsApplication.java
new file mode 100644
index 0000000..cc1334e
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.interceptor.name;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(InterceptedByNameResource.class);
+         add(NameBoundInterceptor.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/NameBoundInterceptor.java b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/NameBoundInterceptor.java
new file mode 100644
index 0000000..0c17811
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/src/main/java/org/glassfish/jersey/tests/performance/interceptor/name/NameBoundInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.interceptor.name;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.SequenceInputStream;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+
+/**
+ * Custom interceptor.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Provider
+@Intercepted
+public class NameBoundInterceptor implements ReaderInterceptor, WriterInterceptor {
+
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+        context.setInputStream(new SequenceInputStream(new ByteArrayInputStream("READ ".getBytes()), context.getInputStream()));
+        return context.proceed();
+    }
+
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+    context.getOutputStream().write("WRITE ".getBytes());
+        context.proceed();
+    }
+}
diff --git a/tests/performance/test-cases/interceptor-name/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/interceptor-name/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..5ffa433
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.interceptor.name.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/interceptor-name/src/test/java/org/glassfish/jersey/tests/performance/interceptor/name/InterceptorTest.java b/tests/performance/test-cases/interceptor-name/src/test/java/org/glassfish/jersey/tests/performance/interceptor/name/InterceptorTest.java
new file mode 100644
index 0000000..84c521e
--- /dev/null
+++ b/tests/performance/test-cases/interceptor-name/src/test/java/org/glassfish/jersey/tests/performance/interceptor/name/InterceptorTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.interceptor.name;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for intercepted text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InterceptorTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("WRITE text", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals("WRITE READ " + original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/mbw-custom-provider/pom.xml b/tests/performance/test-cases/mbw-custom-provider/pom.xml
new file mode 100644
index 0000000..2077e42
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/pom.xml
@@ -0,0 +1,61 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>custom-provider</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-custom-provider-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/JaxRsApplication.java b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/JaxRsApplication.java
new file mode 100644
index 0000000..d865298
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/JaxRsApplication.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.custom;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(PersonResource.class);
+         add(PersonProvider.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/Person.java b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/Person.java
new file mode 100644
index 0000000..105dbb7
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/Person.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.custom;
+
+import java.util.Objects;
+
+/**
+ * Test data bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Person {
+
+    public String name;
+    public int age;
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + Objects.hashCode(this.name);
+        hash = 19 * hash + this.age;
+        hash = 19 * hash + Objects.hashCode(this.address);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (this.age != other.age) {
+            return false;
+        }
+        return (Objects.equals(this.address, other.address));
+    }
+}
diff --git a/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonProvider.java b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonProvider.java
new file mode 100644
index 0000000..8e29399
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonProvider.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.custom;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+/**
+ * Custom message body worker.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Produces("application/person")
+@Consumes("application/person")
+public class PersonProvider implements MessageBodyWriter<Person>, MessageBodyReader<Person> {
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
+        return type == Person.class;
+    }
+
+    @Override
+    public long getSize(Person t, Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
+        return getByteRepresentation(t).length;
+    }
+
+    @Override
+    public void writeTo(Person t,
+                        Class<?> type,
+                        Type type1,
+                        Annotation[] antns,
+                        MediaType mt,
+                        MultivaluedMap<String, Object> mm,
+                        OutputStream out) throws IOException, WebApplicationException {
+        out.write(getByteRepresentation(t));
+    }
+
+    @Override
+    public boolean isReadable(Class<?> type, Type type1, Annotation[] antns, MediaType mt) {
+        return type == Person.class;
+    }
+
+    @Override
+    public Person readFrom(Class<Person> type,
+                           Type type1,
+                           Annotation[] antns,
+                           MediaType mt,
+                           MultivaluedMap<String, String> mm,
+                           InputStream in) throws IOException, WebApplicationException {
+        Person result = new Person();
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        final String nameLine = reader.readLine();
+        result.name = nameLine.substring(nameLine.indexOf(": ") + 2);
+        final String ageLine = reader.readLine();
+        result.age = Integer.parseInt(ageLine.substring(ageLine.indexOf(": ") + 2));
+        final String addressLine = reader.readLine();
+        result.address = addressLine.substring(addressLine.indexOf(": ") + 2);
+
+        return result;
+    }
+
+    private byte[] getByteRepresentation(Person t) {
+        return String.format("name: %s\nage: %d\naddress: %s", t.name, t.age, t.address).getBytes();
+    }
+}
diff --git a/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonResource.java b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonResource.java
new file mode 100644
index 0000000..59196c1
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/src/main/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonResource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.custom;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Person resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes("application/person")
+@Produces("application/person")
+public class PersonResource {
+
+    @POST
+    public Person echo(final Person person) {
+        return person;
+    }
+
+    @PUT
+    public void put(final Person text) {
+    }
+
+    @GET
+    public Person get() {
+        return new Person("Mozart", 21, "Salzburg");
+    }
+}
diff --git a/tests/performance/test-cases/mbw-custom-provider/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-custom-provider/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..db2c84b
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.custom.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-custom-provider/src/test/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonEntityTest.java b/tests/performance/test-cases/mbw-custom-provider/src/test/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonEntityTest.java
new file mode 100644
index 0000000..69aaf37
--- /dev/null
+++ b/tests/performance/test-cases/mbw-custom-provider/src/test/java/org/glassfish/jersey/tests/performance/mbw/custom/PersonEntityTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.custom;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for json resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class PersonEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(PersonProvider.class);
+    }
+
+    @Test
+    public void testGet() {
+        final Person getResponse = target().request().get(Person.class);
+        assertEquals("Mozart", getResponse.name);
+        assertEquals(21, getResponse.age);
+        assertEquals("Salzburg", getResponse.address);
+    }
+
+    @Test
+    public void testPost() {
+        final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth")};
+        for (Person original : testData) {
+            final Person postResponse = target().request().post(Entity.entity(original, "application/person"), Person.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request()
+                .put(Entity.entity(new Person("Jules", 12, "Paris"), "application/person"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson/pom.xml b/tests/performance/test-cases/mbw-json-jackson/pom.xml
new file mode 100644
index 0000000..9121b28
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson/pom.xml
@@ -0,0 +1,65 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-jackson</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-json-jackson-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JaxRsApplication.java b/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JaxRsApplication.java
new file mode 100644
index 0000000..a85709d
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JaxRsApplication.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.json;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(JsonEntityResource.class);
+         add(JacksonFeature.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java b/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java
new file mode 100644
index 0000000..c945291
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.json;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class JsonEntityResource {
+
+    @POST
+    public Person echo(final Person person) {
+        return person;
+    }
+
+    @PUT
+    public void put(final Person person) {
+    }
+
+    @GET
+    public Person get() {
+        return new Person("Wolfgang", 21, "Salzburg");
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java b/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java
new file mode 100644
index 0000000..b3af2b0
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.json;
+
+import java.util.Objects;
+
+/**
+ * Test data bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Person {
+
+    public String name;
+    public int age;
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + Objects.hashCode(this.name);
+        hash = 19 * hash + this.age;
+        hash = 19 * hash + Objects.hashCode(this.address);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (this.age != other.age) {
+            return false;
+        }
+        return (Objects.equals(this.address, other.address));
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-json-jackson/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..88eea6c
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-json-jackson/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java b/tests/performance/test-cases/mbw-json-jackson/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java
new file mode 100644
index 0000000..2a25db3
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.json;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson.JacksonFeature;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for json resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JsonEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(JacksonFeature.class);
+    }
+
+    @Test
+    public void testGet() {
+        final Person getResponse = target().request().get(Person.class);
+        assertEquals("Wolfgang", getResponse.name);
+        assertEquals(21, getResponse.age);
+        assertEquals("Salzburg", getResponse.address);
+    }
+
+    @Test
+    public void testPost() {
+        final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth") };
+        for (Person original : testData) {
+            final Person postResponse = target().request().post(Entity.json(original), Person.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.json(new Person("Jules", 12, "Paris")));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-moxy/pom.xml b/tests/performance/test-cases/mbw-json-moxy/pom.xml
new file mode 100644
index 0000000..0dd8244
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-moxy/pom.xml
@@ -0,0 +1,61 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>json-moxy</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-json-moxy-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JaxRsApplication.java b/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JaxRsApplication.java
new file mode 100644
index 0000000..76f96dc
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JaxRsApplication.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.json;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+         add(JsonEntityResource.class);
+         add(MoxyJsonFeature.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java b/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java
new file mode 100644
index 0000000..b7afc36
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.json;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class JsonEntityResource {
+
+    @POST
+    public Person echo(final Person person) {
+        return person;
+    }
+
+    @PUT
+    public void put(final Person person) {
+    }
+
+    @GET
+    public Person get() {
+        return new Person("Wolfgang", 21, "Salzburg");
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java b/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java
new file mode 100644
index 0000000..b3af2b0
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.json;
+
+import java.util.Objects;
+
+/**
+ * Test data bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class Person {
+
+    public String name;
+    public int age;
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + Objects.hashCode(this.name);
+        hash = 19 * hash + this.age;
+        hash = 19 * hash + Objects.hashCode(this.address);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (this.age != other.age) {
+            return false;
+        }
+        return (Objects.equals(this.address, other.address));
+    }
+}
diff --git a/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..88eea6c
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.json.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-json-moxy/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java b/tests/performance/test-cases/mbw-json-moxy/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java
new file mode 100644
index 0000000..acf2c42
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-moxy/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.json;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for json resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JsonEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(MoxyJsonFeature.class);
+    }
+
+    @Test
+    public void testGet() {
+        final Person getResponse = target().request().get(Person.class);
+        assertEquals("Wolfgang", getResponse.name);
+        assertEquals(21, getResponse.age);
+        assertEquals("Salzburg", getResponse.address);
+    }
+
+    @Test
+    public void testPost() {
+        final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth") };
+        for (Person original : testData) {
+            final Person postResponse = target().request().post(Entity.json(original), Person.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.json(new Person("Jules", 12, "Paris")));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/mbw-kryo/pom.xml b/tests/performance/test-cases/mbw-kryo/pom.xml
new file mode 100644
index 0000000..87d89c0
--- /dev/null
+++ b/tests/performance/test-cases/mbw-kryo/pom.xml
@@ -0,0 +1,61 @@
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>mbw-kryo</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-mbw-kryo-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-kryo</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/JaxRsApplication.java b/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/JaxRsApplication.java
new file mode 100644
index 0000000..26eef29
--- /dev/null
+++ b/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/JaxRsApplication.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.kryo;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
+        add(PersonResource.class);
+    }};
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+
+}
diff --git a/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/Person.java b/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/Person.java
new file mode 100644
index 0000000..8dc8d9e
--- /dev/null
+++ b/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/Person.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.kryo;
+
+import java.util.Objects;
+
+/**
+ * Test data bean.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class Person {
+
+    public String name;
+    public int age;
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + Objects.hashCode(this.name);
+        hash = 19 * hash + this.age;
+        hash = 19 * hash + Objects.hashCode(this.address);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (this.age != other.age) {
+            return false;
+        }
+        return (Objects.equals(this.address, other.address));
+    }
+}
diff --git a/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/PersonResource.java b/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/PersonResource.java
new file mode 100644
index 0000000..f465365
--- /dev/null
+++ b/tests/performance/test-cases/mbw-kryo/src/main/java/org/glassfish/jersey/tests/performance/mbw/kryo/PersonResource.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.kryo;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * Test resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+@Path("/")
+@Consumes("application/x-kryo")
+@Produces("application/x-kryo")
+public class PersonResource {
+
+    @POST
+    public Person echo(final Person person) {
+        return person;
+    }
+
+    @PUT
+    public void put(final Person person) {
+    }
+
+    @GET
+    public Person get() {
+        return new Person("Wolfgang", 21, "Salzburg");
+    }
+
+}
diff --git a/tests/performance/test-cases/mbw-kryo/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-kryo/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..a875b70
--- /dev/null
+++ b/tests/performance/test-cases/mbw-kryo/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.kryo.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-kryo/src/test/java/org/glassfish/jersey/tests/performance/mbw/kryo/PersonResourceTest.java b/tests/performance/test-cases/mbw-kryo/src/test/java/org/glassfish/jersey/tests/performance/mbw/kryo/PersonResourceTest.java
new file mode 100644
index 0000000..bf03e8b
--- /dev/null
+++ b/tests/performance/test-cases/mbw-kryo/src/test/java/org/glassfish/jersey/tests/performance/mbw/kryo/PersonResourceTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.kryo;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for kryo resource.
+ *
+ * @author Libor Kramolis (libor.kramolis at oracle.com)
+ */
+public class PersonResourceTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Override
+    protected void configureClient(final ClientConfig config) {
+    }
+
+    @Test
+    public void testGet() {
+        final Person getResponse = target().request().get(Person.class);
+        assertEquals("Wolfgang", getResponse.name);
+        assertEquals(21, getResponse.age);
+        assertEquals("Salzburg", getResponse.address);
+    }
+
+    @Test
+    public void testPost() {
+        final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth")};
+        for (Person original : testData) {
+            final Person postResponse = target().request()
+                    .post(Entity.entity(original, "application/x-kryo"), Person.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.entity(new Person("Jules", 12, "Paris"),
+                "application/x-kryo"));
+        assertEquals(204, putResponse.getStatus());
+    }
+
+}
diff --git a/tests/performance/test-cases/mbw-text-plain/pom.xml b/tests/performance/test-cases/mbw-text-plain/pom.xml
new file mode 100644
index 0000000..e97d4c7
--- /dev/null
+++ b/tests/performance/test-cases/mbw-text-plain/pom.xml
@@ -0,0 +1,61 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>text-plain</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-text-plain-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/mbw-text-plain/src/main/java/org/glassfish/jersey/tests/performance/mbw/text/JaxRsApplication.java b/tests/performance/test-cases/mbw-text-plain/src/main/java/org/glassfish/jersey/tests/performance/mbw/text/JaxRsApplication.java
new file mode 100644
index 0000000..d6651e6
--- /dev/null
+++ b/tests/performance/test-cases/mbw-text-plain/src/main/java/org/glassfish/jersey/tests/performance/mbw/text/JaxRsApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.text;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
+        add(TextEntityResource.class);
+    }};
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/mbw-text-plain/src/main/java/org/glassfish/jersey/tests/performance/mbw/text/TextEntityResource.java b/tests/performance/test-cases/mbw-text-plain/src/main/java/org/glassfish/jersey/tests/performance/mbw/text/TextEntityResource.java
new file mode 100644
index 0000000..65af72c
--- /dev/null
+++ b/tests/performance/test-cases/mbw-text-plain/src/main/java/org/glassfish/jersey/tests/performance/mbw/text/TextEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.text;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.TEXT_PLAIN)
+@Produces(MediaType.TEXT_PLAIN)
+public class TextEntityResource {
+
+    @POST
+    public String echo(final String text) {
+        return text;
+    }
+
+    @PUT
+    public void put(final String text) {
+    }
+
+    @GET
+    public String get() {
+        return "text";
+    }
+}
diff --git a/tests/performance/test-cases/mbw-text-plain/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-text-plain/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..8476905
--- /dev/null
+++ b/tests/performance/test-cases/mbw-text-plain/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.text.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-text-plain/src/test/java/org/glassfish/jersey/tests/performance/mbw/text/TextEntityTest.java b/tests/performance/test-cases/mbw-text-plain/src/test/java/org/glassfish/jersey/tests/performance/mbw/text/TextEntityTest.java
new file mode 100644
index 0000000..fb6ecb4
--- /dev/null
+++ b/tests/performance/test-cases/mbw-text-plain/src/test/java/org/glassfish/jersey/tests/performance/mbw/text/TextEntityTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.text;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for text plain resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class TextEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final String getResponse = target().request().get(String.class);
+        assertEquals("text", getResponse);
+    }
+
+    @Test
+    public void testPost() {
+        final String[] testData = new String[] {"one", "two", "three" };
+        for (String original : testData) {
+            final String postResponse = target().request().post(Entity.text(original), String.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.text("text"));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/pom.xml b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
new file mode 100644
index 0000000..9644471
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-jaxb/pom.xml
@@ -0,0 +1,60 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>xml-jaxb</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-xml-jaxb-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/JaxRsApplication.java b/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/JaxRsApplication.java
new file mode 100644
index 0000000..94a47ee
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/JaxRsApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.xml;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
+        add(XmlEntityResource.class);
+    }};
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/Person.java b/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/Person.java
new file mode 100644
index 0000000..8d9f6ff
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/Person.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.xml;
+
+import java.util.Objects;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Test data bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+public class Person {
+
+    public String name;
+    public int age;
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + Objects.hashCode(this.name);
+        hash = 19 * hash + this.age;
+        hash = 19 * hash + Objects.hashCode(this.address);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (this.age != other.age) {
+            return false;
+        }
+        return (Objects.equals(this.address, other.address));
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityResource.java b/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityResource.java
new file mode 100644
index 0000000..eaf625c
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-jaxb/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.xml;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.APPLICATION_XML)
+@Produces(MediaType.APPLICATION_XML)
+public class XmlEntityResource {
+
+    @POST
+    public Person echo(final Person person) {
+        return person;
+    }
+
+    @PUT
+    public void put(final Person person) {
+    }
+
+    @GET
+    public Person get() {
+        return new Person("Wolfgang", 21, "Salzburg");
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-xml-jaxb/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..2a80019
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-jaxb/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-xml-jaxb/src/test/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java b/tests/performance/test-cases/mbw-xml-jaxb/src/test/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java
new file mode 100644
index 0000000..8982902
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-jaxb/src/test/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.xml;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for XML resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class XmlEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testGet() {
+        final Person getResponse = target().request().get(Person.class);
+        assertEquals("Wolfgang", getResponse.name);
+        assertEquals(21, getResponse.age);
+        assertEquals("Salzburg", getResponse.address);
+    }
+
+    @Test
+    public void testPost() {
+        final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth") };
+        for (Person original : testData) {
+            final Person postResponse = target().request().post(Entity.xml(original), Person.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.xml(new Person("Jules", 12, "Paris")));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-moxy/pom.xml b/tests/performance/test-cases/mbw-xml-moxy/pom.xml
new file mode 100644
index 0000000..2b40b84
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-moxy/pom.xml
@@ -0,0 +1,60 @@
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>xml-moxy</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-xml-moxy-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/JaxRsApplication.java b/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/JaxRsApplication.java
new file mode 100644
index 0000000..0de51b3
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/JaxRsApplication.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.xml;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+            add(XmlEntityResource.class);
+            add(MoxyXmlFeature.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/Person.java b/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/Person.java
new file mode 100644
index 0000000..2909ac5
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/Person.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.xml;
+
+import java.util.Objects;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Test data bean.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+public class Person {
+
+    public String name;
+    public int age;
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 19 * hash + Objects.hashCode(this.name);
+        hash = 19 * hash + this.age;
+        hash = 19 * hash + Objects.hashCode(this.address);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Person other = (Person) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        if (this.age != other.age) {
+            return false;
+        }
+        return (Objects.equals(this.address, other.address));
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityResource.java b/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityResource.java
new file mode 100644
index 0000000..a28abe7
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-moxy/src/main/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2018 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.performance.mbw.xml;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+@Consumes(MediaType.APPLICATION_XML)
+@Produces(MediaType.APPLICATION_XML)
+public class XmlEntityResource {
+
+    @POST
+    public Person echo(final Person person) {
+        return person;
+    }
+
+    @PUT
+    public void put(final Person person) {
+    }
+
+    @GET
+    public Person get() {
+            return new Person("Wolfgang", 21, "Salzburg");
+    }
+}
diff --git a/tests/performance/test-cases/mbw-xml-moxy/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-xml-moxy/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..2a80019
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-moxy/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.mbw.xml.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/mbw-xml-moxy/src/test/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java b/tests/performance/test-cases/mbw-xml-moxy/src/test/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java
new file mode 100644
index 0000000..9fabc89
--- /dev/null
+++ b/tests/performance/test-cases/mbw-xml-moxy/src/test/java/org/glassfish/jersey/tests/performance/mbw/xml/XmlEntityTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.mbw.xml;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * Test for XML resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class XmlEntityTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.register(MoxyXmlFeature.class);
+    }
+
+    @Test
+    public void testGet() {
+        final Person getResponse = target().request().get(Person.class);
+        assertEquals("Wolfgang", getResponse.name);
+        assertEquals(21, getResponse.age);
+        assertEquals("Salzburg", getResponse.address);
+    }
+
+    @Test
+    public void testPost() {
+        final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth") };
+        for (Person original : testData) {
+            final Person postResponse = target().request().post(Entity.xml(original), Person.class);
+            assertEquals(original, postResponse);
+        }
+    }
+
+    @Test
+    public void testPut() {
+        final Response putResponse = target().request().put(Entity.xml(new Person("Jules", 12, "Paris")));
+        assertEquals(204, putResponse.getStatus());
+    }
+}
diff --git a/tests/performance/test-cases/monitoring/pom.xml b/tests/performance/test-cases/monitoring/pom.xml
new file mode 100644
index 0000000..3b1404b
--- /dev/null
+++ b/tests/performance/test-cases/monitoring/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.glassfish.jersey.test.performance.monitoring</groupId>
+    <artifactId>monitoring</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>monitoring-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.yammer.metrics</groupId>
+            <artifactId>metrics-core</artifactId>
+            <version>2.1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.5</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>14.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+            <version>1.6.1</version>
+        </dependency>
+
+       <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/assembly/zip-with-jars.xml</descriptor>
+                    </descriptors>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <!-- this is used for inheritance merges -->
+                        <phase>package</phase>
+                        <!-- append to the packaging phase. -->
+                        <goals>
+                            <goal>attached</goal>
+                            <!-- goals == mojos -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <inherited>true</inherited>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                    <showWarnings>false</showWarnings>
+                    <fork>false</fork>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/JerseyApp.java b/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/JerseyApp.java
new file mode 100644
index 0000000..a5e97b4
--- /dev/null
+++ b/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/JerseyApp.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.monitoring;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.model.*;
+import org.glassfish.jersey.server.model.Resource;
+
+/**
+ * Application class to start performance test web service at http://localhost:8080/ if the base URI
+ * is not passed via the first command line argument.
+ */
+public class JerseyApp {
+
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+    public static final String ROOT_PATH = "text";
+
+    public static void main(final String[] args) throws Exception {
+            System.out.println("Jersey performance test web service application");
+
+            final URI baseUri = args.length > 0 ? URI.create(args[0]) : BASE_URI;
+            final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, createResourceConfig());
+
+            System.out.println(String.format("Application started.\nTry out %s%s\nHit Ctrl-C to stop it...",
+                    baseUri, ROOT_PATH));
+
+            while (server.isStarted()) {
+                Thread.sleep(600000);
+            }
+    }
+
+    private static ResourceConfig createResourceConfig() {
+        final Set<Resource> resources = new HashSet<>();
+        for (int i = 0; i < 10; i++) {
+            resources.add(Resource.builder(org.glassfish.jersey.tests.performance.monitoring.Resource.class)
+                    .path("" + i)
+                    .build());
+        }
+
+        return new ResourceConfig()
+                .property(ServerProperties.MONITORING_STATISTICS_ENABLED, true)
+                .property(ServerProperties.MONITORING_STATISTICS_MBEANS_ENABLED, true)
+                .registerResources(resources);
+    }
+}
diff --git a/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/Resource.java b/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/Resource.java
new file mode 100644
index 0000000..6956ec8
--- /dev/null
+++ b/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/Resource.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.monitoring;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+/**
+ * @author Michal Gajdos
+ */
+@Path("/")
+@Produces("text/plain")
+public class Resource {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @GET
+    @Path("sub")
+    public String subget() {
+        return "sub-get";
+    }
+
+    @POST
+    public String post(final String post) {
+        return post;
+    }
+
+    @POST
+    @Path("sub")
+    public String subpost(final String post) {
+        return post;
+    }
+
+    @PUT
+    public String put(final String put) {
+        return put;
+    }
+
+    @PUT
+    @Path("sub")
+    public String subput(final String put) {
+        return put;
+    }
+
+    @DELETE
+    @Path("{id}")
+    public String delete(final String delete) {
+        return delete;
+    }
+
+    @DELETE
+    @Path("sub/{id}")
+    public String subdelete(final String delete) {
+        return delete;
+    }
+
+    @Path("locator")
+    public Class<SubResourceLocator> locator() {
+        return SubResourceLocator.class;
+    }
+}
diff --git a/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/SubResourceLocator.java b/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/SubResourceLocator.java
new file mode 100644
index 0000000..0172930
--- /dev/null
+++ b/tests/performance/test-cases/monitoring/src/main/java/org/glassfish/jersey/tests/performance/monitoring/SubResourceLocator.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.monitoring;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+
+/**
+ * @author Michal Gajdos
+ */
+public class SubResourceLocator {
+
+    @GET
+    public String get() {
+        return "get";
+    }
+
+    @POST
+    public String post(final String post) {
+        return post;
+    }
+
+    @PUT
+    public String put(final String put) {
+        return put;
+    }
+
+    @DELETE
+    @Path("{id}")
+    public String delete(final String delete) {
+        return delete;
+    }
+}
diff --git a/tests/performance/test-cases/monitoring/start.sh b/tests/performance/test-cases/monitoring/start.sh
new file mode 100644
index 0000000..5554544
--- /dev/null
+++ b/tests/performance/test-cases/monitoring/start.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (c) 2014, 2018 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
+#
+
+LIBS=$(for l in `ls lib`; do echo -n lib/$l":";done)
+LIBS=`echo $LIBS | sed -es'/:$//'`
+
+java -server -Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m \
+      -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods \
+      -cp $LIBS \
+      -Djava.net.preferIPv4Stack=true \
+      -Dcom.sun.management.jmxremote \
+      -Dcom.sun.management.jmxremote.port=11112 \
+      -Dcom.sun.management.jmxremote.authenticate=false \
+      -Dcom.sun.management.jmxremote.ssl=false \
+      -Dcom.sun.management.jmxremote.local.only=false \
+      org.glassfish.jersey.tests.performance.monitoring.JerseyApp $1
diff --git a/tests/performance/test-cases/param-srl/pom.xml b/tests/performance/test-cases/param-srl/pom.xml
new file mode 100644
index 0000000..2baddfd
--- /dev/null
+++ b/tests/performance/test-cases/param-srl/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2012, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>param-srl</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-param-srl</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/param-srl/src/main/java/org/glassfish/jersey/tests/performance/param/srl/JaxRsApplication.java b/tests/performance/test-cases/param-srl/src/main/java/org/glassfish/jersey/tests/performance/param/srl/JaxRsApplication.java
new file mode 100644
index 0000000..c6dc78d
--- /dev/null
+++ b/tests/performance/test-cases/param-srl/src/main/java/org/glassfish/jersey/tests/performance/param/srl/JaxRsApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.param.srl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
+        add(SrlResource.class);
+    }};
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/param-srl/src/main/java/org/glassfish/jersey/tests/performance/param/srl/SrlResource.java b/tests/performance/test-cases/param-srl/src/main/java/org/glassfish/jersey/tests/performance/param/srl/SrlResource.java
new file mode 100644
index 0000000..2b06e62
--- /dev/null
+++ b/tests/performance/test-cases/param-srl/src/main/java/org/glassfish/jersey/tests/performance/param/srl/SrlResource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.param.srl;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("/")
+public class SrlResource {
+
+    public static class SubResource {
+
+        String p;
+
+        SubResource(String p) {
+            this.p = p;
+        }
+
+        @GET
+        @Produces(MediaType.TEXT_PLAIN)
+        public String get(@MatrixParam("m") final String m, @QueryParam("q") final String q) {
+            return String.format("p=%s, m=%s, q=%s", p, m, q);
+        }
+    }
+
+    @Path("srl/{p}")
+    public SubResource locator(@PathParam("p") String p) {
+        return new SubResource(p);
+    }
+
+    @GET
+    @Path("srm/{p}")
+    @Produces(MediaType.TEXT_PLAIN)
+    public String get(@PathParam("p") final String p, @MatrixParam("m") final String m, @QueryParam("q") final String q) {
+        return String.format("p=%s, m=%s, q=%s", p, m, q);
+    }
+}
diff --git a/tests/performance/test-cases/param-srl/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/param-srl/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..1e27b3b
--- /dev/null
+++ b/tests/performance/test-cases/param-srl/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.param.srl.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/param-srl/src/test/java/org/glassfish/jersey/tests/performance/param/srl/SrlSrmTest.java b/tests/performance/test-cases/param-srl/src/test/java/org/glassfish/jersey/tests/performance/param/srl/SrlSrmTest.java
new file mode 100644
index 0000000..d4ad937
--- /dev/null
+++ b/tests/performance/test-cases/param-srl/src/test/java/org/glassfish/jersey/tests/performance/param/srl/SrlSrmTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.param.srl;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for SRL/SRM resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class SrlSrmTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testSrmSrl() {
+        String[][] testData = new String[][] {
+                {"one", "two", "three"},
+                {"four", "green", "top"},
+                {"bottom", "beauty", "mash"},
+        };
+
+        for (String[] input : testData) {
+            final String srlResponse = target().path("srl").path(input[0]).matrixParam("m", input[1]).queryParam("q", input[2])
+                    .request().get(String.class);
+            final String srmResponse = target().path("srm").path(input[0]).matrixParam("m", input[1]).queryParam("q", input[2])
+                    .request().get(String.class);
+            assertEquals(srlResponse, srmResponse);
+        }
+    }
+}
diff --git a/tests/performance/test-cases/pom.xml b/tests/performance/test-cases/pom.xml
new file mode 100644
index 0000000..6e29029
--- /dev/null
+++ b/tests/performance/test-cases/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests-performance-testcase</name>
+
+    <description>
+        Performance test cases.
+    </description>
+
+    <modules>
+        <module>assemblies</module>
+        <module>filter-dynamic</module>
+        <module>filter-global</module>
+        <module>filter-name</module>
+        <module>interceptor-dynamic</module>
+        <module>interceptor-global</module>
+        <module>interceptor-name</module>
+        <module>mbw-custom-provider</module>
+        <module>mbw-json-jackson</module>
+        <module>mbw-json-moxy</module>
+        <module>mbw-kryo</module>
+        <module>mbw-text-plain</module>
+        <module>mbw-xml-jaxb</module>
+        <module>mbw-xml-moxy</module>
+        <module>param-srl</module>
+        <module>proxy-injection</module>
+    </modules>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-assembly-plugin</artifactId>
+                    <dependencies>
+                        <!-- Contains shared performance test-cases assembly descriptors -->
+                        <dependency>
+                            <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+                            <artifactId>assemblies</artifactId>
+                            <version>${project.version}</version>
+                        </dependency>
+                    </dependencies>
+                    <executions>
+                        <execution>
+                            <id>make-assembly</id>
+                            <!-- this is used for inheritance merges -->
+                            <phase>package</phase>
+                            <!-- append to the packaging phase. -->
+                            <goals>
+                                <goal>attached</goal>
+                                <!-- goals == mojos -->
+                            </goals>
+                            <configuration>
+                                <descriptorRefs>
+                                    <!-- Reference to a descriptor in org.glassfish.jersey.tests.performance.testcases:assemblies module -->
+                                    <descriptorRef>zip-with-jars</descriptorRef>
+                                    <descriptorRef>war</descriptorRef>
+                                </descriptorRefs>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+</project>
diff --git a/tests/performance/test-cases/proxy-injection/pom.xml b/tests/performance/test-cases/proxy-injection/pom.xml
new file mode 100644
index 0000000..2a64052
--- /dev/null
+++ b/tests/performance/test-cases/proxy-injection/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2013, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance.testcases</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>proxy-injection</artifactId>
+    <packaging>jar</packaging>
+
+    <name>jersey-tests-performance-proxy-injection-test</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/FieldInjectedResource.java b/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/FieldInjectedResource.java
new file mode 100644
index 0000000..ff024f7
--- /dev/null
+++ b/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/FieldInjectedResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.proxy.injection;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Test resource to test field injected proxy-able parameters.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("field-injected")
+@Produces(MediaType.TEXT_PLAIN)
+public class FieldInjectedResource {
+
+    @Context
+    SecurityContext securityContext;
+    @Context
+    UriInfo uriInfo;
+    @Context
+    HttpHeaders httpHeaders;
+    @Context
+    Request request;
+
+    @GET
+    @Path("without-parameters")
+    public String getProxy() {
+        return String.format("sc: %s\nui: %s\nhh: %s\nreq: %s",
+                securityContext.getClass(), uriInfo.getClass(), httpHeaders.getClass(), request.getClass());
+    }
+}
diff --git a/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/JaxRsApplication.java b/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/JaxRsApplication.java
new file mode 100644
index 0000000..c396a1a
--- /dev/null
+++ b/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/JaxRsApplication.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.proxy.injection;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.ws.rs.core.Application;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class JaxRsApplication extends Application {
+
+    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>(){
+        {
+            add(FieldInjectedResource.class);
+            add(MethodInjectedResource.class);
+        }
+    };
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        return APP_CLASSES;
+    }
+}
diff --git a/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/MethodInjectedResource.java b/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/MethodInjectedResource.java
new file mode 100644
index 0000000..062e8fd
--- /dev/null
+++ b/tests/performance/test-cases/proxy-injection/src/main/java/org/glassfish/jersey/tests/performance/proxy/injection/MethodInjectedResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013, 2018 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.performance.proxy.injection;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * Test resource to compare resource method with and without injected method proxiable parameters.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@Path("method-injected")
+@Produces(MediaType.TEXT_PLAIN)
+public class MethodInjectedResource {
+
+    @GET
+    @Path("without-parameters")
+    public String getNoProxy() {
+        return "text";
+    }
+
+    @GET
+    @Path("all-parameters")
+    public String getProxy(@Context SecurityContext securityContext,
+                           @Context UriInfo uriInfo,
+                           @Context HttpHeaders httpHeaders,
+                           @Context Request request) {
+        return String.format("sc: %s\nui: %s\nhh: %s\nreq: %s",
+                securityContext.getClass(), uriInfo.getClass(), httpHeaders.getClass(), request.getClass());
+    }
+}
diff --git a/tests/performance/test-cases/proxy-injection/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/proxy-injection/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..da3331d
--- /dev/null
+++ b/tests/performance/test-cases/proxy-injection/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<web-app version="3.1"
+         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
+
+    <servlet>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.glassfish.jersey.tests.performance.proxy.injection.JaxRsApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>JaxRsApplication</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/tests/performance/test-cases/proxy-injection/src/test/java/org/glassfish/jersey/tests/performance/proxy/injection/InjectedResourcesTest.java b/tests/performance/test-cases/proxy-injection/src/test/java/org/glassfish/jersey/tests/performance/proxy/injection/InjectedResourcesTest.java
new file mode 100644
index 0000000..2c9aeba
--- /dev/null
+++ b/tests/performance/test-cases/proxy-injection/src/test/java/org/glassfish/jersey/tests/performance/proxy/injection/InjectedResourcesTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 2018 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.performance.proxy.injection;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for field/method injected resource.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+public class InjectedResourcesTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new JaxRsApplication();
+    }
+
+    @Test
+    public void testSameTypesInjected() {
+        final String methodInjectedResponse = target().path("method-injected").path("all-parameters").request().get(String.class);
+        final String fieldInjectedResponse = target().path("field-injected").path("without-parameters").request()
+                .get(String.class);
+        assertEquals(methodInjectedResponse, fieldInjectedResponse);
+    }
+}
diff --git a/tests/performance/tools/README.html b/tests/performance/tools/README.html
new file mode 100644
index 0000000..3b44849
--- /dev/null
+++ b/tests/performance/tools/README.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+
+    <title>Test data generation tool for Jersey performance tests</title>
+
+    <!-- Bootstrap -->
+    <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
+</head>
+<body>
+
+<div class="container">
+    <div class="row">
+        <div class="col-lg-offset-1 col-lg-10">
+            <header class="page-header">
+                <h1>Test data generation tool for Jersey performance tests</small></h1>
+            </header>
+
+            <p>
+                This module contains a simple tool to generate data sets for performance testing. It generates three
+                distinct media types: xml, json and plain text, each in a variety of predefined data set sizes (1kB,
+                5kB, 10kB, 1MB and optionally 1GB).
+            </p>
+
+            <p>
+                The generation of the largest files (1GB) is disabled by default to save disk space and shorten the
+                generation time. To enable it, change the value of the <pre>GENERATE_ALSO_GIGABYTE_DATASETS</pre>
+            constant in the <pre>TestDataGeneratorApp</pre> class to true.
+            </p>
+
+            <h2>Running the Example</h2>
+
+            <p>Run the example as follows:</p>
+            <blockquote>
+                <pre>mvn clean compile exec:java</pre>
+            </blockquote>
+
+            <p>
+                You can tweak the values of several constants in the <pre>TestDataGeneratorApp</pre> class before
+            launching the generation. For more information, please see the javadocs.
+            The above command launches a Grizzly server with the generation server-side app and runs a client with
+            predefined parameters against it.
+            </p>
+        </div>
+    </div>
+</div>
+
+<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
+</body>
+</html>
diff --git a/tests/performance/tools/pom.xml b/tests/performance/tools/pom.xml
new file mode 100644
index 0000000..9ea3a3a
--- /dev/null
+++ b/tests/performance/tools/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2014, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.glassfish.jersey.tests.performance</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+    <groupId>org.glassfish.jersey.tests.performance.tools</groupId>
+    <artifactId>performance-test-tools</artifactId>
+    <packaging>jar</packaging>
+
+    <name>performance-test-tools</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-grizzly2-http</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-moxy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-server</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>org.glassfish.jersey.tests.performance.tools.TestDataGeneratorApp</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+
+    </build>
+</project>
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/ConstantTestValueGenerator.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/ConstantTestValueGenerator.java
new file mode 100644
index 0000000..eba4e1b
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/ConstantTestValueGenerator.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+/**
+ * Implementation of {@link org.glassfish.jersey.tests.performance.tools.TestValueGenerator} producing constant results.
+ *
+ * Due to its constant nature, this strategy is not suitable for use with {@link java.util.Set}.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class ConstantTestValueGenerator extends TestValueGenerator {
+    private static final int intConstant = 123456789;
+    private static final int charConstant = 'x';
+    private static final String stringConstant = "Hello, world!";
+    private static final long longConstant = 987654321L;
+    private static final float floatConstant = 3.1415f;
+    private static final double doubleConstant = 3.1415926535;
+    private static final byte byteConstant = (byte) 127;
+    private static final short shortConstant = (short) 1024;
+    private static final boolean booleanConstant = true;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getInt() {
+        return intConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public char getChar() {
+        return charConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getString() {
+        return stringConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long getLong() {
+        return longConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public float getFloat() {
+        return floatConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double getDouble() {
+        return doubleConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte getByte() {
+        return byteConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public short getShort() {
+        return shortConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean getBoolean() {
+        return booleanConstant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T getEnum(Class<T> enumType) {
+        T[] values = enumType.getEnumConstants();
+        if (values != null && values.length > 0) {
+            return values[0];
+        }
+        return null;
+    }
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/GenerateForTest.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/GenerateForTest.java
new file mode 100644
index 0000000..4b6fb27
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/GenerateForTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation to mark fields of a bean to be populated by
+ * {@link org.glassfish.jersey.tests.performance.tools.TestValueGenerator} during the test data generation process.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface GenerateForTest {
+    /** specifies how many elements should be created and added to an array/collection */
+    int length() default 1;
+
+    /** due to java type erasure, the type of the collection has to be explicitly specified */
+    Class<?> collectionMemberType() default Object.class;
+
+    /** if the bean contains a collection declared via its interface, the desired implementing class has to be specified */
+    Class<?> implementingClass() default Object.class;
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/Person.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/Person.java
new file mode 100644
index 0000000..9fafa2c
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/Person.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Person data bean, copied from mbw tests and annotated for the test data generation tool.
+ *
+ * @author Jakub Podlesak (jakub.podlesak at oracle.com)
+ */
+@XmlRootElement
+public class Person {
+
+    @GenerateForTest
+    public String name;
+    @GenerateForTest
+    public int age;
+    @GenerateForTest
+    public String address;
+
+    public Person(String name, int age, String address) {
+        this.name = name;
+        this.age = age;
+        this.address = address;
+    }
+
+    public Person() {
+    }
+
+    @Override
+    public String toString() {
+        return "Person@" + Integer.toHexString(hashCode()) + "\nname=" + name + "\nage=" + age + "\nadress=" + address;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Person person = (Person) o;
+
+        if (age != person.age) {
+            return false;
+        }
+        if (address != null ? !address.equals(person.address) : person.address != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(person.name) : person.name != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + age;
+        result = 31 * result + (address != null ? address.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/RandomTestValueGenerator.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/RandomTestValueGenerator.java
new file mode 100644
index 0000000..3f0f961
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/RandomTestValueGenerator.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import java.util.Random;
+
+/**
+ * Implementation of {@link org.glassfish.jersey.tests.performance.tools.TestValueGenerator} producing random results.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class RandomTestValueGenerator extends TestValueGenerator {
+    private static final int MAX_STRING_LENGTH = 50;
+
+    private static final Random random = new Random();
+    private static final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 _";
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T getEnum(Class<T> enumType) {
+        T[] enumValues = enumType.getEnumConstants();
+        return enumValues[random.nextInt(enumValues.length)];
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getInt() {
+        return random.nextInt();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public char getChar() {
+        return (char) random.nextInt(Character.MAX_VALUE + 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getString() {
+        return randomString(random.nextInt(MAX_STRING_LENGTH));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long getLong() {
+        return random.nextLong();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public float getFloat() {
+        return random.nextFloat();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public double getDouble() {
+        return random.nextDouble();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte getByte() {
+        return (byte) random.nextInt(Byte.MAX_VALUE + 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public short getShort() {
+        return (short) random.nextInt(Short.MAX_VALUE + 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean getBoolean() {
+        return random.nextBoolean();
+    }
+
+    private String randomString(int length) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            sb.append(characters.charAt(random.nextInt(characters.length())));
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBean.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBean.java
new file mode 100644
index 0000000..b3f0317
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBean.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Example of a complex testing bean.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+@XmlRootElement
+public class TestBean {
+
+    /* primitives */
+    @GenerateForTest
+    public byte bt;
+    @GenerateForTest
+    public short sh;
+    @GenerateForTest
+    public int i;
+    @GenerateForTest
+    public long l;
+    @GenerateForTest
+    public float f;
+    @GenerateForTest
+    public double d;
+    @GenerateForTest
+    public boolean bl;
+    @GenerateForTest
+    public char c;
+
+    /* primitive wrappers */
+    @GenerateForTest
+    public Byte wrapBt;
+    @GenerateForTest
+    public Short wrapSh;
+    @GenerateForTest
+    public Integer wrapI;
+    @GenerateForTest
+    public Long wrapL;
+    @GenerateForTest
+    public Float wrapF;
+    @GenerateForTest
+    public Double wrapD;
+    @GenerateForTest
+    public Boolean wrapBl;
+    @GenerateForTest
+    public Character wrapC;
+
+    /* arrays */
+    @GenerateForTest(length = 7)
+    public Integer[] array;
+
+    @GenerateForTest
+    public Date date;
+
+    /* 1D - collections */
+    @XmlElementWrapper(name = "StringElements")
+    @XmlElement(name = "StringElement")
+    @GenerateForTest(collectionMemberType = String.class, implementingClass = ArrayList.class, length = 10)
+    public List<String> stringList;
+
+    @XmlElementWrapper(name = "IntegerElements")
+    @XmlElement(name = "IntegerElement")
+    @GenerateForTest(collectionMemberType = Integer.class, length = 5)
+    public HashSet<Integer> integerSet;
+
+    /* enums */
+    @GenerateForTest
+    public TestBeanEnum en;
+
+    /* custom types */
+    @GenerateForTest
+    public TestBeanInfo inner;
+
+    /* recursive */
+    @GenerateForTest
+    public TestBean nextBean;
+
+    /* and what about those? */
+    // CharSequence cs;
+    // Object o;
+    // Map<String, String> map;
+
+    @Override
+    public String toString() {
+        return printContent(0);
+    }
+
+    public String printContent(int level) {
+        String pad = level == 0 ? "" : String.format("%1$" + level + "s", "");
+        StringBuffer buf = new StringBuffer();
+        buf.append(pad + "# TestBean[level=" + level + "]@" + Integer.toHexString(hashCode())).append("\n");
+
+        buf.append(pad + "# Primitives").append("\n");
+        buf.append(pad + "[" + bt + ", " + sh + ", " + i + ", " + l + ", " + f + ", " + d + ", " + bl + ", " + c + "]")
+                .append("\n");
+
+        buf.append(pad + "# Primitives wrappers").append("\n");
+        buf.append(pad + "[" + wrapBt + ", " + wrapSh + ", " + wrapI + ", " + wrapL + ", " + wrapF + ", " + wrapD + ", "
+                + wrapBl + ", " + wrapC + "]").append("\n");
+
+        buf.append(pad + "# Arrays").append("\n");
+        if (array != null) {
+            buf.append(pad + "array: ");
+            for (Integer i : array) {
+                buf.append(i + ", ");
+            }
+            buf.append("\n");
+        }
+
+        buf.append(pad + "# Collections").append("\n");
+        if (stringList != null) {
+            buf.append(pad + "stringList: ");
+            for (String s : stringList) {
+                buf.append(s + ", ");
+            }
+            buf.append("\n");
+        }
+        if (integerSet != null) {
+            buf.append(pad + "integerSet: ");
+            for (Integer i : integerSet) {
+                buf.append(i + ", ");
+            }
+            buf.append("\n");
+        }
+
+        if (date != null) {
+            buf.append(pad + "date: " + date).append("\n");
+        }
+
+        buf.append(pad + "# Enums").append("\n");
+        if (en != null) {
+            buf.append(pad + "en=" + en).append("\n");
+        }
+        buf.append(pad + "# Inner bean").append("\n");
+        if (inner != null) {
+            buf.append(inner.printContent(level + 1));
+        }
+        buf.append("\n");
+        buf.append(pad + "# Recursive bean").append("\n");
+        if (nextBean != null) {
+            buf.append(nextBean.printContent(level + 1));
+        }
+        return buf.toString();
+    }
+
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanCoordinates.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanCoordinates.java
new file mode 100644
index 0000000..7326b98
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanCoordinates.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+/**
+ * Part of a complex test bean example, contained in {@link org.glassfish.jersey.tests.performance.tools.TestBeanInfo}
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestBeanCoordinates {
+    @GenerateForTest
+    public int x;
+    @GenerateForTest
+    public int y;
+
+    public String printContent(int level) {
+        String pad = String.format("%1$" + level + "s", "");
+        return (pad + "# " + this + "\n" + pad + "x:y=" + x + ":" + y);
+    }
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanEnum.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanEnum.java
new file mode 100644
index 0000000..4ba4dc9
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanEnum.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+/**
+ * Nested type of {@link org.glassfish.jersey.tests.performance.tools.TestBean}
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public enum TestBeanEnum {
+    VALUE_1, VALUE_2, VALUE_3;
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanInfo.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanInfo.java
new file mode 100644
index 0000000..5dd6ccc
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestBeanInfo.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+/**
+ * Nested testing bean, contained in {@link org.glassfish.jersey.tests.performance.tools.TestBean}
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestBeanInfo {
+
+    @GenerateForTest
+    public Integer someNumber;
+    @GenerateForTest
+    public int i; // same name as in the main bean, on purpose
+    @GenerateForTest
+    public TestBeanCoordinates coords; // another nested level
+
+    public String printContent(int level) {
+        String pad = String.format("%1$" + level + "s", "");
+        StringBuffer buf = new StringBuffer();
+        buf.append(pad + "# " + this).append("\n");
+        buf.append(pad + "someNumber=" + someNumber).append("\n");
+        buf.append(pad + "i=" + i).append("\n");
+        if (coords != null) {
+            buf.append(coords.printContent(level + 1));
+        }
+        return buf.toString();
+    }
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGenerationStrategy.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGenerationStrategy.java
new file mode 100644
index 0000000..ab6dc47
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGenerationStrategy.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+/**
+ * Enum for test data generation strategies.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public enum TestDataGenerationStrategy {
+    CONSTANT,
+    RANDOM,
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGenerator.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGenerator.java
new file mode 100644
index 0000000..c30efa4
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGenerator.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Instantiates and populates a bean with testing data.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestDataGenerator {
+
+    /**
+     *
+     * @param bean bean to be populated
+     * @param <T> type of the testing bean
+     * @throws ClassNotFoundException
+     * @throws IllegalAccessException
+     * @throws NoSuchMethodException
+     * @throws InvocationTargetException
+     * @throws InstantiationException
+     */
+    public static <T> void populateBeanByAnnotations(T bean) throws ReflectiveOperationException {
+        Field[] fields = bean.getClass().getDeclaredFields();
+        TestValueGenerator generator = TestValueGeneratorFactory.getGenerator(TestDataGenerationStrategy.RANDOM);
+        for (Field field : fields) {
+            GenerateForTest annotation = field.getAnnotation(GenerateForTest.class);
+            if (annotation != null) {
+                field.setAccessible(true);
+                field.set(bean, generator.getValueForType(field.getType(), annotation));
+            }
+        }
+    }
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGeneratorApp.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGeneratorApp.java
new file mode 100644
index 0000000..857c45f
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGeneratorApp.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.ext.ContextResolver;
+
+import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+/**
+ * Application for generating custom testing data files for Jersey performance tests.
+ *
+ * <p>Creates set of files containing plain text, json and xml in various predefined sizes:
+ * 1kB, 5kB, 10kB, 1MB and optionally 1GB. The 1GB file has to be enabled by changing the constant
+ * {@code GENERATE_ALSO_GIGABYTE_DATASETS} to true.</p>
+ *
+ * <p>The sizes are the MINIMAL sizes, the generation stops after reaching the given size,
+ * but does not truncate the most recently
+ * generated entity. For simple testing beans the size difference from the predefined treshold is minimal,
+ * whereas for very complex testing beans the difference can be significant.</p>
+ *
+ * <p>MOXy is used for creating XML and JSON from the testing beans.</p>
+ *
+ * <p>Run the generation by invoking {@code mvn clean compile} and {@code mvn exec:java} commands in the module root folder.</p>
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestDataGeneratorApp {
+
+    /** change the value to true to generate also 1GB files; can be time consuming and takes additional 3GB of disk
+     * space (1GB json, 1GB xml and 1GB text) */
+    private static final boolean GENERATE_ALSO_GIGABYTE_DATASETS = false;
+
+    /** path where the generated files should be stored, including the final slash */
+    private static final String FILE_PATH = "";
+
+    /** specifies how the outputs generated from one bean should be separated from each other in the output files */
+    public static final String ENTITY_SEPARATOR = "\n\n";
+
+    private static final Logger LOG = Logger.getLogger(TestDataGeneratorApp.class.getName());
+    private static final URI BASE_URI = URI.create("http://localhost:8080/");
+    private static URI baseUri;
+
+    public static void main(final String[] args) throws Exception {
+        baseUri = args.length > 0 ? URI.create(args[0]) : BASE_URI;
+        final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, createApp());
+
+        LOG.info("Jersey performance test data generation - application started.");
+
+        try {
+            // generate text files - 1kb, 5kb, 10kb, 1MB and optionally 1GB
+            generateFile("simple/text", 1024, FILE_PATH + "custom-1kb.text");
+            generateFile("simple/text", 5 * 1024, FILE_PATH + "custom-5kb.text");
+            generateFile("simple/text", 10 * 1024, FILE_PATH + "custom-10kb.text");
+            generateFile("simple/text", 1024 * 1024, FILE_PATH + "custom-1MB.text");
+            if (GENERATE_ALSO_GIGABYTE_DATASETS) {
+                generateFile("text", 1024 * 1024 * 1024, FILE_PATH + "custom-1GB.text");
+            }
+
+            // generate json files - 1kb, 5kb, 10kb, 1MB and optionally 1GB
+            generateFile("simple/json", 1024, FILE_PATH + "custom-1kb.json");
+            generateFile("simple/json", 5 * 1024, FILE_PATH + "custom-5kb.json");
+            generateFile("simple/json", 10 * 1024, FILE_PATH + "custom-10kb.json");
+            generateFile("simple/json", 1024 * 1024, FILE_PATH + "custom-1MB.json");
+            if (GENERATE_ALSO_GIGABYTE_DATASETS) {
+                generateFile("simple/json", 1024 * 1024 * 1024, FILE_PATH + "custom-1GB.json");
+            }
+
+            // generate xml files - 1kb, 5kb, 10kb, 1MB and optionally 1GB
+            generateFile("simple/xml", 1024, FILE_PATH + "custom-1kb.xml");
+            generateFile("simple/xml", 5 * 1024, FILE_PATH + "custom-5kb.xml");
+            generateFile("simple/xml", 10 * 1024, FILE_PATH + "custom-10kb.xml");
+            generateFile("simple/xml", 1024 * 1024, FILE_PATH + "custom-1MB.xml");
+            if (GENERATE_ALSO_GIGABYTE_DATASETS) {
+                generateFile("simple/xml", 1024 * 1024 * 1024, FILE_PATH + "custom-1GB.json");
+            }
+        } catch (final IOException e) {
+            LOG.log(Level.SEVERE, "An error occurred during test data generation. ", e);
+        }
+        server.shutdown();
+    }
+
+    public static ResourceConfig createApp() {
+        return new ResourceConfig()
+                .packages("org.glassfish.jersey.tests.performance.tools")
+                .register(createMoxyJsonResolver());
+    }
+
+    public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
+        final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig();
+        final Map<String, String> namespacePrefixMapper = new HashMap<>(1);
+        namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
+        moxyJsonConfig.setNamespacePrefixMapper(namespacePrefixMapper).setNamespaceSeparator(':');
+        return moxyJsonConfig.resolver();
+    }
+
+    public static void generateFile(final String resourceRelativeUrl, final int minimalSize, final String fileName)
+            throws IOException {
+        LOG.info("Generating file " + fileName);
+        final Client client = ClientBuilder.newClient();
+        final WebTarget target = client.target(baseUri).path("generate").path(resourceRelativeUrl);
+
+        final File file = new File(fileName);
+        final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
+                new FileOutputStream(file), Charset.forName("UTF-8")));
+
+        int actualSize = 0;
+        while (actualSize < minimalSize) {
+            final String response = target.request().get(String.class);
+            writer.write(response + ENTITY_SEPARATOR);
+            actualSize += response.length();
+        }
+
+        writer.flush();
+        writer.close();
+    }
+
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGeneratorResource.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGeneratorResource.java
new file mode 100644
index 0000000..19d72b6
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestDataGeneratorResource.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Resource for generating data for performance tests.
+ * For more information, see {@link TestDataGeneratorApp}
+ */
+@Path("generate")
+public class TestDataGeneratorResource {
+
+    private static Logger LOG = Logger.getLogger(TestDataGeneratorResource.class.getName());
+
+    /**
+     * Generates plain text based data from a complex testing bean.
+     * @return result of a call to a {@link Object#toString()} method of the populated bean.
+     */
+    @GET
+    @Path("complex/text")
+    public String generateComplexText() {
+        return getComplexTestBean().toString();
+    }
+
+    /**
+     * Generates json based data from a complex testing bean.
+     * @return the bean to be converted to json
+     */
+    @GET
+    @Path("complex/json")
+    @Produces(MediaType.APPLICATION_JSON)
+    public TestBean generateComplexJson() {
+        return getComplexTestBean();
+    }
+
+    /**
+     * Generates xml based data from a complex testing bean.
+     * @return the bean to be converted to xml
+     */
+    @GET
+    @Path("complex/xml")
+    @Produces(MediaType.APPLICATION_XML)
+    public TestBean generateComplexXml() {
+        return getComplexTestBean();
+    }
+
+    /**
+     * Generates plain text based data from a simple testing bean.
+     * @return result of a call to a {@link Object#toString()} method of the populated bean.
+     */
+    @GET
+    @Path("simple/text")
+    public String generateSimpleText() {
+        return getSimpleTestBean().toString();
+    }
+
+    /**
+     * Generates json based data from a simple testing bean.
+     * @return the bean to be converted to json
+     */
+    @GET
+    @Path("simple/json")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Person generateSimpleJson() {
+        return getSimpleTestBean();
+    }
+
+    /**
+     * Generates xml based data from a simple testing bean.
+     * @return the bean to be converted to xml
+     */
+    @GET
+    @Path("simple/xml")
+    @Produces(MediaType.APPLICATION_XML)
+    public Person generateSimpleXml() {
+        return getSimpleTestBean();
+    }
+
+    private TestBean getComplexTestBean() {
+        TestBean bean = new TestBean();
+        try {
+            TestDataGenerator.populateBeanByAnnotations(bean);
+        } catch (Exception e) {
+            LOG.log(Level.SEVERE, "Error while populating the testing bean.", e);
+        }
+        return bean;
+    }
+
+    private Person getSimpleTestBean() {
+        Person bean = new Person();
+        try {
+            TestDataGenerator.populateBeanByAnnotations(bean);
+        } catch (Exception e) {
+            LOG.log(Level.SEVERE, "Error while populating the testing bean.", e);
+        }
+        return bean;
+    }
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestValueGenerator.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestValueGenerator.java
new file mode 100644
index 0000000..779acfb
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestValueGenerator.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * Abstract class for test data generation.
+ *
+ * <p>Creates the pattern for different generation strategies.
+ * Contains the (limited) logic for class graph walk-through.<p/>
+ * <p>Every field which should be populated must be annotated by
+ * {@link org.glassfish.jersey.tests.performance.tools.GenerateForTest}</p>
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public abstract class TestValueGenerator {
+
+    private static final int MAX_RECURSION_LEVEL = 5;
+
+    private static final Logger log = Logger.getLogger(TestValueGenerator.class.getName());
+
+    /** returns testing data int value */
+    public abstract int getInt();
+
+    /** returns testing data char value */
+    public abstract char getChar();
+
+    /** returns testing data String value */
+    public abstract String getString();
+
+    /** returns testing data long value */
+    public abstract long getLong();
+
+    /** returns testing data float value */
+    public abstract float getFloat();
+
+    /** returns testing data double value */
+    public abstract double getDouble();
+
+    /** returns testing data byte value */
+    public abstract byte getByte();
+
+    /** returns testing data short value */
+    public abstract short getShort();
+
+    /** returns testing data boolean value */
+    public abstract boolean getBoolean();
+
+    /** returns testing data enum value */
+    public abstract <T> T getEnum(Class<T> enumType);
+
+    protected Object handlePrimitivesAndWrappers(Class<?> type) {
+        if (type.isAssignableFrom(String.class)) {
+            return getString();
+        }
+        if (type.isAssignableFrom(Integer.class) || type.isAssignableFrom(int.class)) {
+            return getInt();
+        }
+        if (type.isAssignableFrom(Character.class) || type.isAssignableFrom(char.class)) {
+            return getChar();
+        }
+        if (type.isAssignableFrom(Float.class) || type.isAssignableFrom(float.class)) {
+            return getFloat();
+        }
+        if (type.isAssignableFrom(Long.class) || type.isAssignableFrom(long.class)) {
+            return getLong();
+        }
+        if (type.isAssignableFrom(Double.class) || type.isAssignableFrom(double.class)) {
+            return getDouble();
+        }
+        if (type.isAssignableFrom(Byte.class) || type.isAssignableFrom(byte.class)) {
+            return getByte();
+        }
+        if (type.isAssignableFrom(Short.class) || type.isAssignableFrom(short.class)) {
+            return getShort();
+        }
+        if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
+            return getBoolean();
+        }
+        return null;
+    }
+
+    protected Object handleCollections(Class<?> type,
+                                       GenerateForTest annotation,
+                                       int recursionLevel) throws ReflectiveOperationException {
+        int testDataLength = annotation.length();
+        Class<?> collectionMemberType = annotation.collectionMemberType();
+        Class<?> collectionType = type;
+        if (collectionType.isInterface()) {
+            collectionType = annotation.implementingClass();
+            if (collectionType.equals(Object.class)) {
+                throw new IllegalArgumentException("Unable to instantiate collection - interface was used for the "
+                        + "declaration and parameter 'implementingClass' not set.");
+            }
+        }
+        Object collection = collectionType.newInstance();
+        for (int i = 0; i < testDataLength; i++) {
+            // recursively resolve value for collection members
+            Object o = getValueForType(collectionMemberType, null, recursionLevel + 1);
+            // and add it to the collection instance
+            Method addMethod = type.getDeclaredMethod("add", Object.class);
+            addMethod.invoke(collection, o);
+        }
+        return collection;
+    }
+
+    protected Object handleArrays(Class<?> type, GenerateForTest annotation, int recursionLevel)
+            throws ReflectiveOperationException {
+
+        int testDataLength = annotation.length();
+        Class<?> arrayMemberType = type.getComponentType();
+        Object array = Array.newInstance(arrayMemberType, testDataLength);
+        for (int i = 0; i < testDataLength; i++) {
+            Object o = getValueForType(arrayMemberType, null, recursionLevel + 1);
+            Array.set(array, i, o);
+        }
+        return array;
+    }
+
+    public Object getValueForType(Class<?> type, GenerateForTest annotation) throws ReflectiveOperationException {
+        return getValueForType(type, annotation, 0);
+    }
+
+    protected Object getValueForType(Class<?> type, GenerateForTest annotation, int recursionLevel)
+            throws ReflectiveOperationException {
+
+        // handle primitives and wrapper classes
+        Object primitiveOrWrapper = handlePrimitivesAndWrappers(type);
+        if (primitiveOrWrapper != null) {
+            return primitiveOrWrapper;
+        }
+
+        // Handle collections
+        if (Collection.class.isAssignableFrom(type)) {
+            if (annotation != null) {
+                return handleCollections(type, annotation, recursionLevel);
+            } else {
+                return null;
+            }
+        }
+
+        // handle maps (unsupported)
+        if (Map.class.isAssignableFrom(type)) {
+            throw new IllegalArgumentException("Maps are not supported.");
+        }
+
+        // handle enums
+        if (type.isEnum()) {
+            return getEnum(type);
+        }
+
+        // handle arrays
+        if (type.isArray()) {
+            if (annotation != null) {
+                return handleArrays(type, annotation, recursionLevel);
+            } else {
+                return null;
+            }
+        }
+
+        // after selecting-out "all" the other possibilities, we are probably handling a custom inner-bean
+        // create the inner type
+        if (recursionLevel == MAX_RECURSION_LEVEL) {
+            log.fine("Maximum recursion level (" + recursionLevel + ") reached. Ignoring the field.");
+            return null;
+        }
+        Object innerBean = type.newInstance();
+        Field[] fields = type.getDeclaredFields();
+        for (Field field : fields) {
+            GenerateForTest subFieldAnnotation = field.getAnnotation(GenerateForTest.class);
+            if (subFieldAnnotation != null) {
+                field.setAccessible(true);
+                // recursively gather the values for the containing fields and set it
+                field.set(innerBean, getValueForType(field.getType(), subFieldAnnotation, recursionLevel + 1));
+            }
+        }
+        return innerBean;
+    }
+
+}
diff --git a/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestValueGeneratorFactory.java b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestValueGeneratorFactory.java
new file mode 100644
index 0000000..cb268b5
--- /dev/null
+++ b/tests/performance/tools/src/main/java/org/glassfish/jersey/tests/performance/tools/TestValueGeneratorFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2018 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.performance.tools;
+
+/**
+ * Factory for {@link org.glassfish.jersey.tests.performance.tools.TestValueGenerator} implementations.
+ *
+ * @author Adam Lindenthal (adam.lindenthal at oracle.com)
+ */
+public class TestValueGeneratorFactory {
+    /**
+     * Returns instance of {@link org.glassfish.jersey.tests.performance.tools.TestValueGenerator}
+     * @param strategy data generation strategy
+     * @return generator instance
+     */
+    public static TestValueGenerator getGenerator(TestDataGenerationStrategy strategy) {
+        if (strategy == TestDataGenerationStrategy.CONSTANT) {
+            return new ConstantTestValueGenerator();
+        } else if (strategy == TestDataGenerationStrategy.RANDOM) {
+            return new RandomTestValueGenerator();
+        }
+        throw new IllegalArgumentException("Requested TestDataGenerationStrategy does not exist.");
+    }
+}
diff --git a/tests/pom.xml b/tests/pom.xml
new file mode 100644
index 0000000..9d6e92a
--- /dev/null
+++ b/tests/pom.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2011, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>project</artifactId>
+    <packaging>pom</packaging>
+    <name>jersey-tests</name>
+
+    <description>
+        Test modules containing test scenario types that involve multiple implementation
+        modules (integration, functional, etc. tests).
+    </description>
+
+    <modules>
+        <module>e2e</module>
+        <module>e2e-client</module>
+        <module>e2e-core-common</module>
+        <module>e2e-entity</module>
+        <module>e2e-inject</module>
+        <module>e2e-server</module>
+        <module>e2e-testng</module>
+        <module>integration</module>
+        <module>mem-leaks</module>
+        <module>osgi</module>
+        <module>stress</module>
+        <module>performance</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-install-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.sonatype.plugins</groupId>
+                        <artifactId>nexus-staging-maven-plugin</artifactId>
+                        <configuration>
+                            <skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/tests/stress/pom.xml b/tests/stress/pom.xml
new file mode 100644
index 0000000..e9c359c
--- /dev/null
+++ b/tests/stress/pom.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2015, 2018 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.tests</groupId>
+        <artifactId>project</artifactId>
+        <version>2.28-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>stress</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-tests-stress</name>
+
+    <description>
+
+        Jersey stress tests
+
+        This module may contain tests which execution is limited by time (or a fault). This may lead to a considerable longer
+        build of Jersey.
+
+    </description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>-Xmx2048m ${surefire.coverage.argline}</argLine>
+                    <forkMode>always</forkMode>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>sonar</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <!-- disable JaCoCo listener because it's not working with <forkMode>always</fork> (<reuseForks>false</reuseForks> respectively)
+                                https://jira.sonarsource.com/browse/SONARJAVA-728 (https://github.com/SonarSource/sonar-java/pull/324) -->
+                                <properties combine.self="override" />
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tests/stress/src/test/java/org/glassfish/jersey/server/internal/monitoring/MultiThreadingAggregatedReservoirTest.java b/tests/stress/src/test/java/org/glassfish/jersey/server/internal/monitoring/MultiThreadingAggregatedReservoirTest.java
new file mode 100644
index 0000000..95fff83
--- /dev/null
+++ b/tests/stress/src/test/java/org/glassfish/jersey/server/internal/monitoring/MultiThreadingAggregatedReservoirTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2015, 2018 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.server.internal.monitoring;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
+
+import org.glassfish.jersey.server.internal.monitoring.core.TimeReservoir;
+import org.glassfish.jersey.server.internal.monitoring.core.UniformTimeSnapshot;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Multi Threading concurrency test of Jersey monitoring internals.
+ *
+ * @author Stepan Vavra (stepan.vavra at oracle.com)
+ */
+public class MultiThreadingAggregatedReservoirTest {
+
+    private static final Logger LOGGER = Logger.getLogger(MultiThreadingAggregatedReservoirTest.class.getName());
+
+    private static final int PRODUCER_COUNT = 5;
+    private static final int CONSUMER_COUNT = 5;
+
+    /**
+     * Note that more than 5 seconds may require more than 1G heap memory.
+     */
+    private static final int TEST_DURATION_MILLIS = 10_000;
+    private static final int SHUTDOWN_TIMEOUT_SECONDS = 120;
+    private static final double DELTA = 0.0001;
+
+    private final AtomicInteger incrementer = new AtomicInteger(0);
+
+    private final ExecutorService producerExecutorService = Executors
+            .newFixedThreadPool(PRODUCER_COUNT, new ThreadFactoryBuilder().setDaemon(true).build());
+
+    private final ExecutorService consumerExecutorService = Executors
+            .newFixedThreadPool(CONSUMER_COUNT, new ThreadFactoryBuilder().setDaemon(true).build());
+
+    private final long startTime = System.nanoTime();
+    private final TimeUnit startUnitTime = TimeUnit.NANOSECONDS;
+
+    private final AggregatingTrimmer trimmer =
+            new AggregatingTrimmer(startTime(), startUnitTime, 10, TimeUnit.MICROSECONDS);
+    private final SlidingWindowTimeReservoir time10usReservoir =
+            new SlidingWindowTimeReservoir(10, TimeUnit.MICROSECONDS,
+                                           startTime(), startUnitTime, trimmer);
+    private final AggregatedSlidingWindowTimeReservoir time1DayAggregatedReservoir =
+            new AggregatedSlidingWindowTimeReservoir(1,
+                                                     TimeUnit.DAYS,
+                                                     startTime(), startUnitTime, trimmer);
+    private final AggregatedSlidingWindowTimeReservoir time10DaysAggregatedReservoir =
+            new AggregatedSlidingWindowTimeReservoir(
+                    10, TimeUnit.DAYS,
+                    startTime(), startUnitTime, trimmer);
+    private final List<AggregatedSlidingWindowTimeReservoir> aggregatedTimeReservoirs =
+            new CopyOnWriteArrayList<>(
+                    Arrays.asList(
+                            new AggregatedSlidingWindowTimeReservoir(1, TimeUnit.SECONDS, startTime(),
+                                                                     startUnitTime, trimmer),
+                            time1DayAggregatedReservoir,
+                            time10DaysAggregatedReservoir
+                    ));
+
+    /**
+     * Determines the start time of the test.
+     *
+     * @return The start time of the test. Must be a constant value.
+     */
+    protected long startTime() {
+        return startTime;
+    }
+
+    private volatile boolean doShutdown = false;
+
+    /**
+     * Runs {@link #PRODUCER_COUNT} producers that update {@link #time10usReservoir} 10 microseconds sliding window reservoir with
+     * sequentially increasing values generated by {@link @incrementer}. This sliding window updates 1 day aggregated sliding
+     * window and also 10 days aggregated sliding window ({@link #time1DayAggregatedReservoir} and {@link
+     * #time10DaysAggregatedReservoir} respectively). In the meantime, {@link #CONSUMER_COUNT} consumers retrieve snapshots from
+     * the aggregated window in order to increase the level of concurrency.
+     *
+     * @throws InterruptedException If any of the thread was interrupted and the test result won't be reliable
+     */
+    @Test
+    public void parallelProducersAndConsumersTestingAggregatedSlidingWindows() throws InterruptedException {
+
+        executeInParallel(consumerExecutorService, CONSUMER_COUNT, new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    LOGGER.info("Consumer starting.");
+                    while (!doShutdown && !Thread.currentThread().isInterrupted()) {
+
+                        aggregatedTimeReservoirs.get(ThreadLocalRandom.current().nextInt(aggregatedTimeReservoirs.size()))
+                                                .getSnapshot(System.nanoTime(), TimeUnit.NANOSECONDS);
+                        Thread.sleep(100);
+                    }
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                } finally {
+                    LOGGER.info("Consumer terminating.");
+                }
+            }
+        });
+
+        executeInParallel(producerExecutorService, PRODUCER_COUNT, new Runnable() {
+            @Override
+            public void run() {
+                LOGGER.info("Producer starting.");
+                while (!doShutdown) {
+                    final int value = incrementer.incrementAndGet();
+                    time10usReservoir.update((long) value, System.nanoTime(), TimeUnit.NANOSECONDS);
+                }
+                LOGGER.info("Producer terminating.");
+            }
+        });
+
+        Thread.sleep(TEST_DURATION_MILLIS);
+        LOGGER.info("Shutting down...");
+
+        doShutdown = true;
+        producerExecutorService.shutdown();
+        consumerExecutorService.shutdown();
+        Assert.assertTrue("Consumer tasks didn't terminated peacefully, aborting this test.",
+                          consumerExecutorService.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+        Assert.assertTrue("Producer tasks didn't terminated peacefully, aborting this test.",
+                          producerExecutorService.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+
+        final long snapshotTime = System.nanoTime();
+        final long sum = (long) incrementer.get() * (incrementer.get() + 1) / 2;
+
+        LOGGER.info("Integer reached: " + incrementer.get());
+
+        checkInNanos(time1DayAggregatedReservoir, snapshotTime, incrementer.get(), 1, incrementer.get(),
+                     (double) sum / incrementer.get(), snapshotTime - startTime());
+        checkInNanos(time10DaysAggregatedReservoir, snapshotTime, incrementer.get(), 1, incrementer.get(),
+                     (double) sum / incrementer.get(), snapshotTime - startTime());
+    }
+
+    private void executeInParallel(final Executor consumerExecutorService, final int count, final Runnable runnable) {
+        for (int i = 0; i < count; ++i) {
+            consumerExecutorService.execute(runnable);
+        }
+    }
+
+    /**
+     * Shutdown the producer executor service.
+     */
+    @After
+    public void shutdownProducers() {
+        producerExecutorService.shutdownNow();
+    }
+
+    /**
+     * Shutdown the consumer executor service.
+     */
+    @After
+    public void shutdownConsumers() {
+        consumerExecutorService.shutdownNow();
+    }
+
+    /**
+     * Checks whether the snapshot of given reservoir exhibits with expected measurements.
+     *
+     * @param reservoir        The reservoir to assert.
+     * @param snapshotTime     The time for which to get the snapshot
+     * @param expectedSize     Expected size of the snapshot
+     * @param expectedMin      Expected minimum
+     * @param expectedMax      Expected maximum
+     * @param expectedMean     Expected mean
+     * @param expectedInterval Expected interval
+     */
+    private static void checkInNanos(final TimeReservoir reservoir,
+                                     final long snapshotTime,
+                                     final long expectedSize,
+                                     final long expectedMin,
+                                     final long expectedMax,
+                                     final double expectedMean, final long expectedInterval) {
+        final UniformTimeSnapshot snapshot = reservoir.getSnapshot(snapshotTime, TimeUnit.NANOSECONDS);
+
+        assertEquals("Total count does not match!", expectedSize, snapshot.size());
+        assertEquals("Min exec time does not match!", expectedMin, snapshot.getMin());
+        assertEquals("Max exec time does not match!", expectedMax, snapshot.getMax());
+        assertEquals("Average exec time does not match!", expectedMean, snapshot.getMean(), DELTA);
+        assertEquals("Expected interval does not match!", expectedInterval, snapshot.getTimeInterval(TimeUnit.NANOSECONDS));
+    }
+}