From 61f65fc75a17ce79825b65de22b3a4eed392f3a0 Mon Sep 17 00:00:00 2001 From: R Searls Date: Mon, 7 Dec 2020 12:46:49 -0500 Subject: [PATCH] [RESTEASY-1865] ported proposed code changes to branch --- .../resteasy/core/ResourceMethodRegistry.java | 4 + .../resourcefactory/SingletonResource.java | 3 +- .../resteasy/spi/ResteasyProviderFactory.java | 77 ++++++++-------- .../resteasy/test/core/DispatcherTest.java | 87 +++++++++++++++++++ 4 files changed, 132 insertions(+), 39 deletions(-) create mode 100644 testsuite/unit-tests/src/test/java/org/jboss/resteasy/test/core/DispatcherTest.java diff --git a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/ResourceMethodRegistry.java b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/ResourceMethodRegistry.java index 70f5e18d162..c849d225a96 100755 --- a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/ResourceMethodRegistry.java +++ b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/core/ResourceMethodRegistry.java @@ -281,6 +281,10 @@ protected void register(ResourceFactory rf, String base, ResourceClass resourceC { processMethod(rf, base, method); } + + if (rf instanceof SingletonResource) { + providerFactory.registerSingletonResource((SingletonResource) rf); + } } /** diff --git a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/plugins/server/resourcefactory/SingletonResource.java b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/plugins/server/resourcefactory/SingletonResource.java index 37560dda867..f0ea4fbb1e5 100755 --- a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/plugins/server/resourcefactory/SingletonResource.java +++ b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/plugins/server/resourcefactory/SingletonResource.java @@ -36,8 +36,7 @@ public void registered(ResteasyProviderFactory factory) factory.getInjectorFactory().createPropertyInjector(resourceClass, factory).inject(obj); } - public Object createResource(HttpRequest request, HttpResponse response, ResteasyProviderFactory factory) - { + public Object createResource(HttpRequest request, HttpResponse response, ResteasyProviderFactory factory) { return obj; } diff --git a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/spi/ResteasyProviderFactory.java b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/spi/ResteasyProviderFactory.java index 45a8bc358a9..8379fd4eefe 100644 --- a/resteasy-jaxrs/src/main/java/org/jboss/resteasy/spi/ResteasyProviderFactory.java +++ b/resteasy-jaxrs/src/main/java/org/jboss/resteasy/spi/ResteasyProviderFactory.java @@ -31,6 +31,7 @@ import org.jboss.resteasy.plugins.delegates.NewCookieHeaderDelegate; import org.jboss.resteasy.plugins.delegates.UriHeaderDelegate; import org.jboss.resteasy.plugins.providers.RegisterBuiltin; +import org.jboss.resteasy.plugins.server.resourcefactory.SingletonResource; import org.jboss.resteasy.resteasy_jaxrs.i18n.LogMessages; import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages; import org.jboss.resteasy.specimpl.LinkBuilderImpl; @@ -108,6 +109,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; @@ -292,6 +294,9 @@ public interface CloseableContext extends AutoCloseable { private boolean initialized = false; protected ResourceBuilder resourceBuilder; + // RESTEASY-1865 resource factories for singleton resources + private Map, SingletonResource> singletonResourceFactories; + private StatisticsControllerImpl statisticsController = new StatisticsControllerImpl(); public ResteasyProviderFactory() @@ -400,6 +405,7 @@ protected void initialize(ResteasyProviderFactory parent) builtinsRegistered = false; registerBuiltins = true; + singletonResourceFactories = new HashMap<>(); injectorFactory = parent == null ? new InjectorFactoryImpl() : parent.getInjectorFactory(); registerDefaultInterceptorPrecedences(); addHeaderDelegateIfAbsent(MediaType.class, new MediaTypeHeaderDelegate()); @@ -2846,6 +2852,16 @@ public T injectedInstance(Class clazz) return (T) obj; } + public void registerSingletonResource(SingletonResource resource) + { + singletonResourceFactories.put(resource.getScannableClass(), resource); + } + + private boolean isSingletonResource(Class resourceClass) + { + return singletonResourceFactories.containsKey(resourceClass); + } + /** * Property and constructor injection using the InjectorFactory. * @@ -2855,68 +2871,55 @@ public T injectedInstance(Class clazz) * @param type * @return instance of type T */ - public T injectedInstance(Class clazz, HttpRequest request, HttpResponse response) - { - Constructor constructor = PickConstructor.pickSingletonConstructor(clazz); + public T injectedInstance(Class clazz, HttpRequest request, HttpResponse response) { Object obj = null; - if (constructor == null) + + if (isSingletonResource(clazz)) { + SingletonResource factory = singletonResourceFactories.get(clazz); + obj = CompletableFuture.completedFuture(factory.createResource(request, response, this)).toCompletableFuture().getNow(null); + } + else + { + Constructor constructor = PickConstructor.pickSingletonConstructor(clazz); + + if (constructor == null) { // TODO this is solely to pass the TCK. This is WRONG WRONG WRONG! I'm challenging. if (false)//if (clazz.isAnonymousClass()) { constructor = clazz.getDeclaredConstructors()[0]; constructor.setAccessible(true); - if (!Modifier.isStatic(clazz.getModifiers())) - { + if (!Modifier.isStatic(clazz.getModifiers())) { Object[] args = {null}; - try - { + try { obj = constructor.newInstance(args); - } - catch (InstantiationException e) - { + } catch (InstantiationException e) { throw new RuntimeException(e); - } - catch (IllegalAccessException e) - { + } catch (IllegalAccessException e) { throw new RuntimeException(e); - } - catch (InvocationTargetException e) - { + } catch (InvocationTargetException e) { throw new RuntimeException(e); } - } - else - { - try - { + } else { + try { obj = constructor.newInstance(); - } - catch (InstantiationException e) - { + } catch (InstantiationException e) { throw new RuntimeException(e); - } - catch (IllegalAccessException e) - { + } catch (IllegalAccessException e) { throw new RuntimeException(e); - } - catch (InvocationTargetException e) - { + } catch (InvocationTargetException e) { throw new RuntimeException(e); } } - } - else - { + } else { throw new IllegalArgumentException(Messages.MESSAGES.unableToFindPublicConstructorForClass(clazz.getName())); } - } - else - { + } else { ConstructorInjector constructorInjector = getInjectorFactory().createConstructor(constructor, this); obj = constructorInjector.construct(request, response); } + } PropertyInjector propertyInjector = getInjectorFactory().createPropertyInjector(clazz, this); propertyInjector.inject(request, response, obj); diff --git a/testsuite/unit-tests/src/test/java/org/jboss/resteasy/test/core/DispatcherTest.java b/testsuite/unit-tests/src/test/java/org/jboss/resteasy/test/core/DispatcherTest.java new file mode 100644 index 00000000000..7c388f928b1 --- /dev/null +++ b/testsuite/unit-tests/src/test/java/org/jboss/resteasy/test/core/DispatcherTest.java @@ -0,0 +1,87 @@ +package org.jboss.resteasy.test.core; + +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.util.Date; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.container.ResourceContext; +import javax.ws.rs.core.Context; + +import org.jboss.resteasy.core.Dispatcher; +import org.jboss.resteasy.mock.MockDispatcherFactory; +import org.jboss.resteasy.mock.MockHttpRequest; +import org.jboss.resteasy.mock.MockHttpResponse; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class DispatcherTest { + + private Dispatcher dispatcher; + + + @Before + public void before() { + dispatcher = MockDispatcherFactory.createDispatcher(); + } + + // @see https://issues.jboss.org/browse/RESTEASY-1865 + @Test + public void testSingletonResource() throws URISyntaxException, UnsupportedEncodingException { + dispatcher.getRegistry().addSingletonResource(new ParentResource()); + dispatcher.getRegistry().addSingletonResource(new ChildResource("I'm singleton child")); + + MockHttpRequest request = MockHttpRequest.get("/parent"); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertEquals(200, response.getStatus()); + assertEquals("I'm singleton child", response.getContentAsString()); + } + + + + + @Path("/parent") + public static class ParentResource { + + @Context + public ResourceContext resourceCtx; + + + @GET + public String get() { + ChildResource child = resourceCtx.getResource(ChildResource.class); + return child.get(); + } + + } + + @Path("child") + public static class ChildResource { + + private final String name; + + + @SuppressWarnings("unused") // Not used if RESTEASY-1865 is fixed + public ChildResource() { + this.name = "I'm new child created on " + new Date(); + } + + public ChildResource(final String name) { + this.name = name; + } + + + @GET + public String get() { + return name; + } + + } + +}