Support for @ApplicationPath in SE

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
index fabff44..7ddb233 100644
--- a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
@@ -252,7 +252,9 @@
         // Map the path to the processor.
         final ServerConfiguration config = server.getServerConfiguration();
         if (handler != null) {
-            final String path = uri.getPath().replaceAll("/{2,}", "/");
+            final String appPath = handler.getApplicationHandler().getConfiguration().getApplicationPath();
+            final String uriPath = appPath == null ? uri.getPath() : uri.getPath() + "/" + appPath;
+            final String path = uriPath.replaceAll("/{2,}", "/");
 
             final String contextPath = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
             config.addHttpHandler(handler, HttpHandlerRegistration.bulder().contextPath(contextPath).build());
diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
index 2403672..6b8b8c1 100644
--- a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
+++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java
@@ -228,15 +228,19 @@
             throw new IllegalArgumentException(LocalizationMessages.ERROR_CONTAINER_URI_SCHEME_UNKNOWN(uri));
         }
 
-        final String path = uri.getPath();
-        if (path == null) {
+        final String _path = uri.getPath();
+        if (_path == null) {
             throw new IllegalArgumentException(LocalizationMessages.ERROR_CONTAINER_URI_PATH_NULL(uri));
-        } else if (path.isEmpty()) {
+        } else if (_path.isEmpty()) {
             throw new IllegalArgumentException(LocalizationMessages.ERROR_CONTAINER_URI_PATH_EMPTY(uri));
-        } else if (path.charAt(0) != '/') {
+        } else if (_path.charAt(0) != '/') {
             throw new IllegalArgumentException(LocalizationMessages.ERROR_CONTAINER_URI_PATH_START(uri));
         }
 
+        final String appPath = handler.getApplicationHandler().getConfiguration().getApplicationPath();
+        final String uriPath = appPath == null ? _path : _path + "/" + appPath;
+        final String path = uriPath.replaceAll("/{2,}", "/");
+
         final int port = (uri.getPort() == -1)
                 ? (isHttp ? Container.DEFAULT_HTTP_PORT : Container.DEFAULT_HTTPS_PORT)
                 : uri.getPort();
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
index 3207c93..dbe8dc7 100644
--- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -51,6 +51,7 @@
 class JerseyServerHandler extends ChannelInboundHandlerAdapter {
 
     private final URI baseUri;
+    private final String applicationPath;
     private final NettyInputStream nettyInputStream = new NettyInputStream();
     private final NettyHttpContainer container;
     private final ResourceConfig resourceConfig;
@@ -65,9 +66,20 @@
      * @param container Netty container implementation.
      */
     public JerseyServerHandler(URI baseUri, NettyHttpContainer container, ResourceConfig resourceConfig) {
+        this(baseUri, null, container, resourceConfig);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param baseUri   base {@link URI} of the container (includes context path, if any).
+     * @param container Netty container implementation.
+     */
+    public JerseyServerHandler(URI baseUri, String applicationPath, NettyHttpContainer container, ResourceConfig resourceConfig) {
         this.baseUri = baseUri;
         this.container = container;
         this.resourceConfig = resourceConfig;
+        this.applicationPath = applicationPath;
     }
 
     @Override
@@ -145,7 +157,11 @@
     private ContainerRequest createContainerRequest(ChannelHandlerContext ctx, HttpRequest req) {
 
         String s = req.uri().startsWith("/") ? req.uri().substring(1) : req.uri();
-        URI requestUri = URI.create(baseUri + ContainerUtils.encodeUnsafeCharacters(s));
+        final String baseUriStr = baseUri.toString();
+        final String base = applicationPath == null || applicationPath.isEmpty()
+                ? baseUriStr
+                : baseUriStr.substring(0, baseUriStr.length() - applicationPath.length() - 1);
+        final URI requestUri = URI.create(base + ContainerUtils.encodeUnsafeCharacters(s));
 
         ContainerRequest requestContext = new ContainerRequest(
                 baseUri, requestUri, req.method().name(), getSecurityContext(ctx),
diff --git a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerInitializer.java b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerInitializer.java
index d492449..b6d73fd 100644
--- a/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerInitializer.java
+++ b/containers/netty-http/src/main/java/org/glassfish/jersey/netty/httpserver/JerseyServerInitializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -44,6 +44,7 @@
 class JerseyServerInitializer extends ChannelInitializer<SocketChannel> {
 
     private final URI baseUri;
+    private final String applicationPath;
     private final SslContext sslCtx;
     private final NettyHttpContainer container;
     private final boolean http2;
@@ -72,7 +73,14 @@
      */
     public JerseyServerInitializer(URI baseUri, SslContext sslCtx, NettyHttpContainer container, ResourceConfig resourceConfig,
                                    boolean http2) {
-        this.baseUri = baseUri;
+        applicationPath = container.getApplicationHandler().getConfiguration().getApplicationPath();
+        final String uriPath = applicationPath == null
+                ? baseUri.toString()
+                : baseUri.toString() + "/" + applicationPath + "/";
+        final int doubleSlash = uriPath.indexOf("/") + 2;
+        final String path = uriPath.substring(0, doubleSlash) + uriPath.substring(doubleSlash).replaceAll("/{2,}", "/");
+
+        this.baseUri = URI.create(path);
         this.sslCtx = sslCtx;
         this.container = container;
         this.resourceConfig = resourceConfig;
@@ -96,7 +104,7 @@
             }
             p.addLast(new HttpServerCodec());
             p.addLast(new ChunkedWriteHandler());
-            p.addLast(new JerseyServerHandler(baseUri, container, resourceConfig));
+            p.addLast(new JerseyServerHandler(baseUri, applicationPath, container, resourceConfig));
         }
     }
 
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java b/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java
index a2202ae..e433adb 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/JerseySeBootstrapConfiguration.java
@@ -136,7 +136,7 @@
         private final Map<String, Object> properties = new HashMap<>();
 
         private Builder() {
-            this.properties.put(SeBootstrap.Configuration.PROTOCOL, "http");
+            this.properties.put(SeBootstrap.Configuration.PROTOCOL, "HTTP"); // upper case mandated by javadoc
             this.properties.put(SeBootstrap.Configuration.HOST, "localhost");
             this.properties.put(SeBootstrap.Configuration.PORT, -1); // Auto-select port 80 for HTTP or 443 for HTTPS
             this.properties.put(SeBootstrap.Configuration.ROOT_PATH, "/");
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
index 92240f4..291b76f 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
@@ -25,11 +25,13 @@
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
 
+import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.RuntimeType;
 import jakarta.ws.rs.core.Application;
@@ -57,6 +59,7 @@
 import org.glassfish.jersey.server.internal.scanning.FilesScanner;
 import org.glassfish.jersey.server.internal.scanning.PackageNamesScanner;
 import org.glassfish.jersey.server.model.Resource;
+import org.glassfish.jersey.uri.UriComponent;
 
 
 /**
@@ -998,6 +1001,32 @@
     }
 
     /**
+     * Returns encoded value of {@link ApplicationPath} annotation of the Application corresponding
+     * with this ResourceConfig or {@code null} when the annotation is not present.
+     *
+     * @return Returns encoded value of {@link ApplicationPath} annotation of the Application
+     * corresponding with this ResourceConfig.
+     */
+    public final String getApplicationPath() {
+        final Application application;
+        if (ResourceConfig.class.isInstance(_getApplication())) {
+              final Application unwrap = unwrapCustomRootApplication((ResourceConfig) _getApplication());
+              application = unwrap != null ? unwrap : _getApplication();
+        } else {
+            application = _getApplication();
+        }
+        final ApplicationPath appPath = application.getClass().getAnnotation(ApplicationPath.class);
+        final String value;
+        if (appPath != null && !appPath.value().isEmpty() && !appPath.value().trim().equals("/")) {
+            final String val = appPath.value().trim();
+            value = UriComponent.encode(val.startsWith("/") ? val.substring(1) : val, UriComponent.Type.PATH);
+        } else {
+            value = null;
+        }
+        return value;
+    }
+
+    /**
      * Allows overriding the {@link #getApplication()} method functionality in {@link WrappingResourceConfig}.
      *
      * @return JAX-RS application corresponding with this ResourceConfig.
diff --git a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java
index a8c089e..d5921a5 100644
--- a/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java
+++ b/examples/cdi-webapp/src/main/java/org/glassfish/jersey/examples/cdi/resources/MyApplication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -13,7 +13,6 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.core.Application;
 
 /**
@@ -21,7 +20,6 @@
  *
  * @author Jonathan Benoit
  */
-@ApplicationPath("/*")
 public class MyApplication extends Application {
     @Override
     public Set<Class<?>> getClasses() {
diff --git a/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java
index 66d545f..fb0b1fe 100644
--- a/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java
+++ b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/DefaultMethodResourceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -35,7 +35,7 @@
      */
     @Test
     public void testDefaultMethods() {
-        final WebTarget defaultMethodTarget = target("default-method");
+        final WebTarget defaultMethodTarget = target("j8").path("default-method");
 
         // test default method with no @Path annotation
         String response = defaultMethodTarget.request().get(String.class);
@@ -51,7 +51,7 @@
      */
     @Test
     public void testImplementingClass() throws Exception {
-        final String response = target("default-method").path("class").request().get(String.class);
+        final String response = target("j8").path("default-method").path("class").request().get(String.class);
         assertEquals("class", response);
     }
 }
diff --git a/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java
index fddf375..5c04e06 100644
--- a/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java
+++ b/examples/java8-webapp/src/test/java/org/glassfish/jersey/examples/java8/LambdaResourceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -36,7 +36,7 @@
      */
     @Test
     public void testLambdas() {
-        final WebTarget target = target("lambdas/{p}");
+        final WebTarget target = target("j8").path("lambdas/{p}");
 
         // test default method with no @Path annotation
         String response = target.resolveTemplate("p", "test").request().get(String.class);
diff --git a/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java b/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java
index 7239f9b..99dad4b 100644
--- a/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java
+++ b/examples/managed-client-simple-webapp/src/test/java/org/glassfish/jersey/examples/managedclientsimple/ManagedClientSimpleTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -45,7 +45,7 @@
 
     @Test
     public void testManagedClientSimple() throws Exception {
-        final WebTarget resource = target().path("client");
+        final WebTarget resource = target("app").path("client");
         Response response;
 
         response = resource.path("animals").request(MediaType.TEXT_PLAIN).get();
diff --git a/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java b/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java
index 6ee42d6..cdabad0 100644
--- a/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java
+++ b/examples/sse-item-store-jaxrs-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jaxrs/JaxrsItemStoreResourceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -125,7 +125,7 @@
     @Test
     public void testItemsStore() throws Exception {
         final List<String> items = Collections.unmodifiableList(Arrays.asList("foo", "bar", "baz"));
-        final WebTarget itemsTarget = target("items");
+        final WebTarget itemsTarget = target("resources").path("items");
         final CountDownLatch latch = new CountDownLatch(items.size() * MAX_LISTENERS * 2); // countdown on all events
         final List<Queue<Integer>> indexQueues = new ArrayList<>(MAX_LISTENERS);
         final SseEventSource[] sources = new SseEventSource[MAX_LISTENERS];
@@ -193,7 +193,7 @@
      */
     @Test
     public void testEventSourceReconnect() throws Exception {
-        final WebTarget itemsTarget = target("items");
+        final WebTarget itemsTarget = target("resources").path("items");
         final CountDownLatch latch = new CountDownLatch(MAX_ITEMS * MAX_LISTENERS * 2); // countdown only on new item events
         final List<Queue<String>> receivedQueues = new ArrayList<>(MAX_LISTENERS);
         final SseEventSource[] sources = new SseEventSource[MAX_LISTENERS];
diff --git a/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java b/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java
index a03597c..f59b438 100644
--- a/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java
+++ b/examples/sse-item-store-jersey-webapp/src/test/java/org/glassfish/jersey/examples/sseitemstore/jersey/JerseyItemStoreResourceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -101,7 +101,7 @@
                 "foo",
                 "bar",
                 "baz"));
-        final WebTarget itemsTarget = target("items");
+        final WebTarget itemsTarget = target("resources").path("items");
         final CountDownLatch latch = new CountDownLatch(items.size() * MAX_LISTENERS * 2); // countdown on all events
         final List<Queue<Integer>> indexQueues = new ArrayList<>(MAX_LISTENERS);
         final EventSource[] sources = new EventSource[MAX_LISTENERS];
@@ -174,9 +174,8 @@
      * @throws Exception in case of a test failure.
      */
     @Test
-    @Ignore //TODO - remove after jacartification
     public void testEventSourceReconnect() throws Exception {
-        final WebTarget itemsTarget = target("items");
+        final WebTarget itemsTarget = target("resources").path("items");
         final CountDownLatch latch = new CountDownLatch(MAX_ITEMS * MAX_LISTENERS * 2); // countdown only on new item events
         final List<Queue<String>> receivedQueues = new ArrayList<>(MAX_LISTENERS);
         final EventSource[] sources = new EventSource[MAX_LISTENERS];
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ApplicationPathTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ApplicationPathTest.java
new file mode 100644
index 0000000..6239551
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ApplicationPathTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.container;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
+import org.glassfish.jersey.test.jdkhttp.JdkHttpServerTestContainerFactory;
+import org.glassfish.jersey.test.netty.NettyTestContainerFactory;
+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;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@RunWith(Parameterized.class)
+public class ApplicationPathTest extends JerseyContainerTest {
+
+
+    private static final List<TestContainerFactory> FACTORIES = listContainerFactories(
+            new GrizzlyTestContainerFactory(),
+            new JdkHttpServerTestContainerFactory(),
+            new NettyTestContainerFactory()
+    );
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<TestContainerFactory[]> parameters() throws Exception {
+        return FACTORIES.stream().map(input -> new TestContainerFactory[]{input}).collect(Collectors.toList());
+    }
+
+    @ApplicationPath("applicationpath")
+    public static class ApplicationPathTestApplication extends Application {
+        @Override
+        public Set<Class<?>> getClasses() {
+            return Collections.singleton(ApplicationPathResourceTest.class);
+        }
+    }
+
+    @Path("/resource")
+    public static class ApplicationPathResourceTest {
+        @GET
+        public String hello() {
+            return "HelloWorld!";
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ApplicationPathTestApplication();
+    }
+
+    @Test
+    public void testApplicationPath() {
+        try (Response response = target("applicationpath").path("resource").request().get()) {
+            Assert.assertEquals(200, response.getStatus());
+            Assert.assertEquals(new ApplicationPathResourceTest().hello(), response.readEntity(String.class));
+        }
+
+        try (Response response = target("").path("resource").request().get()) {
+            Assert.assertEquals(404, response.getStatus());
+        }
+    }
+
+}
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResourceConfigApplicationPathTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResourceConfigApplicationPathTest.java
new file mode 100644
index 0000000..9ad95a7
--- /dev/null
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/container/ResourceConfigApplicationPathTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.container;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+import org.glassfish.jersey.server.ResourceConfig;
+
+public class ResourceConfigApplicationPathTest extends ApplicationPathTest {
+    @Override
+    protected Application configure() {
+
+        return new ResourceConfigApplicationPathTestResourceConfig()
+                .register(super.configure().getClasses().iterator().next());
+    }
+
+    @ApplicationPath("/applicationpath")
+    public static class ResourceConfigApplicationPathTestResourceConfig extends ResourceConfig {
+
+    }
+
+}
diff --git a/tests/integration/cdi-integration/cdi-log-check/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java b/tests/integration/cdi-integration/cdi-log-check/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
index 7cd3b13..eef274d 100644
--- a/tests/integration/cdi-integration/cdi-log-check/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
+++ b/tests/integration/cdi-integration/cdi-log-check/src/main/java/org/glassfish/jersey/tests/cdi/resources/MyApplication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,14 +18,12 @@
 
 import org.glassfish.jersey.server.ServerProperties;
 
-import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.core.Application;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-@ApplicationPath("/*")
 public class MyApplication extends Application {
 
     @Override
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java b/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
index 00005af..deff079 100644
--- a/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
+++ b/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2019 Payara Foundation and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -30,7 +30,6 @@
 
 import jakarta.inject.Inject;
 
-import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.core.Application;
 
 /**
@@ -39,7 +38,6 @@
  * @author Jonathan Benoit
  * @author Patrik Dudits
  */
-@ApplicationPath("main")
 @ApplicationScoped
 public class MainApplication extends Application {
 
diff --git a/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java b/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java
index fd4b0d5..c5161ee 100644
--- a/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java
+++ b/tests/integration/cdi-integration/cdi-test-webapp/src/main/java/org/glassfish/jersey/tests/cdi/resources/SecondaryApplication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,15 +18,7 @@
 
 import java.util.HashSet;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Logger;
-
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.PreDestroy;
 import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.enterprise.inject.spi.BeanManager;
-import jakarta.inject.Inject;
-import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.core.Application;
 
 /**
@@ -37,7 +29,6 @@
  *
  * @author Jakub Podlesak
  */
-@ApplicationPath("secondary")
 @ApplicationScoped
 public class SecondaryApplication extends Application {
 
diff --git a/tests/integration/jersey-3992/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java b/tests/integration/jersey-3992/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
index 4bfed39..0a8563c 100644
--- a/tests/integration/jersey-3992/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
+++ b/tests/integration/jersey-3992/src/main/java/org/glassfish/jersey/tests/cdi/resources/MainApplication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018 Payara Foundation and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -30,7 +30,6 @@
 
 import jakarta.inject.Inject;
 
-import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.core.Application;
 
 /**
@@ -38,7 +37,6 @@
  *
  * @author Jonathan Benoit
  */
-@ApplicationPath("main")
 @ApplicationScoped
 public class MainApplication extends Application {