From 65f4f1b0eb9c4e7510450acfd04095af7e60dd27 Mon Sep 17 00:00:00 2001 From: jansupol Date: Tue, 31 Aug 2021 22:13:18 +0200 Subject: [PATCH] Cache Application#getSingletons not be called twice Signed-off-by: jansupol --- .../jersey/server/ResourceConfig.java | 21 +++-- .../tests/api/ApplicationCachingTest.java | 88 +++++++++++++++++++ 2 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ApplicationCachingTest.java 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 361acc4c8d..1b13d056e3 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -42,6 +42,7 @@ import org.glassfish.jersey.internal.inject.Binder; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.spi.AutoDiscoverable; +import org.glassfish.jersey.internal.util.Producer; import org.glassfish.jersey.internal.util.PropertiesHelper; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.internal.util.Tokenizer; @@ -1194,16 +1195,18 @@ private RuntimeConfig(final ResourceConfig original) { this.application = original; final Application customRootApp = ResourceConfig.unwrapCustomRootApplication(original); - if (customRootApp != null) { - registerComponentsOf(customRootApp); - } + + final Set rootSingletons = customRootApp != null ? registerComponentsOf(customRootApp) : null; originalRegistrations = Collections.newSetFromMap(new IdentityHashMap<>()); originalRegistrations.addAll(super.getRegisteredClasses()); + // Do not call the same Application#getSingletons twice + final Set origSingletons = customRootApp != null ? rootSingletons : original.getSingletons(); + // Register externally provided instances. final Set externalInstances = - original.getSingletons().stream() + origSingletons.stream() .filter(external -> !originalRegistrations.contains(external.getClass())) .collect(Collectors.toSet()); @@ -1216,10 +1219,10 @@ private RuntimeConfig(final ResourceConfig original) { registerClasses(externalClasses); } - private void registerComponentsOf(final Application application) { - Errors.processWithException(new Runnable() { + private Set registerComponentsOf(final Application application) { + return Errors.process(new Producer>() { @Override - public void run() { + public Set call() { // First register instances that should take precedence over classes // in case of duplicate registrations final Set singletons = application.getSingletons(); @@ -1248,8 +1251,10 @@ public void run() { }) .collect(Collectors.toSet())); } + return singletons; } }); + } private RuntimeConfig(final Application application) { diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ApplicationCachingTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ApplicationCachingTest.java new file mode 100644 index 0000000000..6286924997 --- /dev/null +++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/ApplicationCachingTest.java @@ -0,0 +1,88 @@ +/* + * 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.api; + +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Assert; +import org.junit.Test; + +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.core.Application; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +public class ApplicationCachingTest extends JerseyTest { + + private static AtomicInteger singletonCounter = new AtomicInteger(0); + + public static class ApplicationCachingTestFilter implements ContainerRequestFilter { + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + } + } + + @Path("/") + public static class ApplicationCachingTestResource { + @GET + public String get() { + return "GET"; + } + } + + public static class OneTimeCalledApplication extends Application { + @Override + public Map getProperties() { + Map map = new HashMap<>(); + map.put(ServerProperties.WADL_FEATURE_DISABLE, true); + return map; + } + + @Override + public Set getSingletons() { + singletonCounter.incrementAndGet(); + return Collections.singleton(new ApplicationCachingTestFilter()); + } + + @Override + public Set> getClasses() { + return Collections.singleton(ApplicationCachingTestResource.class); + } + } + + @Override + protected Application configure() { + return new OneTimeCalledApplication(); + } + + @Test + public void testOneTimeCalled() { + try (Response r = target().request().get()) { + Assert.assertEquals(200, r.getStatus()); + } + Assert.assertEquals(1, singletonCounter.get()); + } +}