From c6a273ac0bb38a12c97a503abb65c9c3eb18b07b Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 19 Apr 2023 22:02:00 +0200 Subject: [PATCH] Better support inheritance in Resource Methods Signed-off-by: jansupol --- .../internal/util/ReflectionHelper.java | 28 ++++++-- .../AbstractJavaResourceMethodDispatcher.java | 16 ++++- ...sourceMethodDispatcherInheritanceTest.java | 70 +++++++++++++++++++ 3 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java index b4519ac4af..c272637af2 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -23,6 +23,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Array; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Member; @@ -444,7 +445,7 @@ public Object run() { * @see AccessController#doPrivileged(java.security.PrivilegedAction) */ public static PrivilegedAction setAccessibleMethodPA(final Method m) { - if (Modifier.isPublic(m.getModifiers())) { + if (isPublic(m)) { return NoOpPrivilegedACTION; } @@ -460,6 +461,24 @@ public Object run() { }; } + /** + * Return {@code true} iff the method is public. + * @param clazz The method in question + * @return {@code true} if mod includes the public modifier; {@code false} otherwise. + */ + public static boolean isPublic(Class clazz) { + return Modifier.isPublic(clazz.getModifiers()); + } + + /** + * Return {@code true} iff the executable is public. + * @param executable The executable in question + * @return {@code true} if the executable includes the public modifier; {@code false} otherwise. + */ + public static boolean isPublic(Executable executable) { + return Modifier.isPublic(executable.getModifiers()); + } + /** * Get the list of classes that represent the type arguments of a * {@link ParameterizedType parameterized} input type. @@ -879,8 +898,7 @@ public static Collection> getAnnotationTypes(final A * @return {@code true} if the method is {@code getter}, {@code false} otherwise. */ public static boolean isGetter(final Method method) { - if (method.getParameterTypes().length == 0 - && Modifier.isPublic(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && isPublic(method)) { final String methodName = method.getName(); if (methodName.startsWith("get") && methodName.length() > 3) { @@ -921,7 +939,7 @@ public static GenericType genericTypeFor(final Object instance) { * @return {@code true} if the method is {@code setter}, {@code false} otherwise. */ public static boolean isSetter(final Method method) { - return Modifier.isPublic(method.getModifiers()) + return isPublic(method) && void.class.equals(method.getReturnType()) && method.getParameterTypes().length == 1 && method.getName().startsWith("set"); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java index 853e717846..cb1fdee24d 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -32,6 +32,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; +import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.message.internal.OutboundMessageContext; import org.glassfish.jersey.message.internal.TracingLogger; @@ -68,12 +69,23 @@ abstract class AbstractJavaResourceMethodDispatcher implements ResourceMethodDis AbstractJavaResourceMethodDispatcher(final Invocable resourceMethod, final InvocationHandler methodHandler, final ConfiguredValidator validator) { - this.method = resourceMethod.getDefinitionMethod(); + this.method = getPublic(resourceMethod.getHandlingMethod(), resourceMethod.getDefinitionMethod()); this.methodHandler = methodHandler; this.resourceMethod = resourceMethod; this.validator = validator; } + private Method getPublic(Method handlingMethod, Method definitionMethod) { + if (handlingMethod == definitionMethod) { + return handlingMethod; + } + + boolean publicHandling = ReflectionHelper.isPublic(handlingMethod) + && ReflectionHelper.isPublic(handlingMethod.getDeclaringClass()); + + return publicHandling ? handlingMethod : definitionMethod; + } + @Override public final Response dispatch(Object resource, ContainerRequest request) throws ProcessingException { Response response = null; diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java new file mode 100644 index 0000000000..7d3b8a50d9 --- /dev/null +++ b/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceMethodDispatcherInheritanceTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.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.model; + +import org.glassfish.jersey.server.ApplicationHandler; +import org.glassfish.jersey.server.ContainerResponse; +import org.glassfish.jersey.server.RequestContextBuilder; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import java.util.concurrent.ExecutionException; + +public class ResourceMethodDispatcherInheritanceTest { + public interface ResourceIfc1 { + @GET + public void get(); + } + + @Path("/") + static class ResourceClass1 implements ResourceIfc1 { + public void get() { + + } + } + + interface ResourceIfc2 { + @GET + public void get(); + } + + @Path("/") + public static class ResourceClass2 implements ResourceIfc2 { + public void get() { + + } + } + + @Test + public void testInheritedMethodPublicClass() throws ExecutionException, InterruptedException { + ApplicationHandler app = new ApplicationHandler(new ResourceConfig(ResourceClass2.class)); + ContainerResponse response; + response = app.apply(RequestContextBuilder.from("/", "GET").accept("text/plain").build()).get(); + Assertions.assertEquals(204, response.getStatus()); + } + + @Test + public void testInheritedMethodPublicIface() throws ExecutionException, InterruptedException { + ApplicationHandler app = new ApplicationHandler(new ResourceConfig(ResourceClass1.class)); + ContainerResponse response; + response = app.apply(RequestContextBuilder.from("/", "GET").accept("text/plain").build()).get(); + Assertions.assertEquals(204, response.getStatus()); + } +}