diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/IntrospectionModeller.java b/core-server/src/main/java/org/glassfish/jersey/server/model/IntrospectionModeller.java index 665894babb..7393a83418 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/IntrospectionModeller.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/IntrospectionModeller.java @@ -85,6 +85,8 @@ final class IntrospectionModeller { // introspected annotated JAX-RS resource class private final Class handlerClass; + // introspected annotated JAX-RS resource instance + private final Object handlerInstance; // validation flag private final boolean disableValidation; @@ -92,10 +94,12 @@ final class IntrospectionModeller { * Create a new introspection modeller for a given JAX-RS resource class. * * @param handlerClass JAX-RS resource (handler) class. + * @param handlerInstance JAX-RS resource (handler) instance. * @param disableValidation if set to {@code true}, then any model validation checks will be disabled. */ - public IntrospectionModeller(Class handlerClass, boolean disableValidation) { + public IntrospectionModeller(Class handlerClass, Object handlerInstance, boolean disableValidation) { this.handlerClass = handlerClass; + this.handlerInstance = handlerInstance; this.disableValidation = disableValidation; } @@ -341,7 +345,7 @@ private void addResourceMethods( .encodedParameters(encodedParameters || am.isAnnotationPresent(Encoded.class)) .nameBindings(defaultNameBindings) .nameBindings(am.getAnnotations()) - .handledBy(handlerClass, am.getMethod()) + .handledBy(handlerClass, handlerInstance, am.getMethod()) .handlingMethod(am.getDeclaredMethod()) .handlerParameters(resourceClassParameters) .extended(extended || am.isAnnotationPresent(ExtendedResource.class)); @@ -371,7 +375,7 @@ private void addSubResourceMethods( .encodedParameters(encodedParameters || am.isAnnotationPresent(Encoded.class)) .nameBindings(defaultNameBindings) .nameBindings(am.getAnnotations()) - .handledBy(handlerClass, am.getMethod()) + .handledBy(handlerClass, handlerInstance, am.getMethod()) .handlingMethod(am.getDeclaredMethod()) .handlerParameters(resourceClassParameters) .extended(extended || am.isAnnotationPresent(ExtendedResource.class)); @@ -396,7 +400,7 @@ private void addSubResourceLocators( builder.addMethod() .encodedParameters(encodedParameters || am.isAnnotationPresent(Encoded.class)) - .handledBy(handlerClass, am.getMethod()) + .handledBy(handlerClass, handlerInstance, am.getMethod()) .handlingMethod(am.getDeclaredMethod()) .handlerParameters(resourceClassParameters) .extended(extended || am.isAnnotationPresent(ExtendedResource.class)); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java b/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java index afd7076510..a871c06271 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java @@ -771,7 +771,7 @@ public static Builder builder(Class resourceClass) { * class does not represent a resource. */ public static Builder builder(Class resourceClass, boolean disableValidation) { - final Builder builder = new IntrospectionModeller(resourceClass, disableValidation).createResourceBuilder(); + final Builder builder = new IntrospectionModeller(resourceClass, null, disableValidation).createResourceBuilder(); return builder.isEmpty() ? null : builder; } @@ -797,7 +797,22 @@ public static Resource from(Class resourceClass) { * class does not represent a resource. */ public static Resource from(Class resourceClass, boolean disableValidation) { - final Builder builder = new IntrospectionModeller(resourceClass, disableValidation).createResourceBuilder(); + return from(resourceClass, null, disableValidation); + } + + /** + * Create a resource model initialized by introspecting an annotated + * JAX-RS resource class. + * + * @param resourceClass resource class to be modelled. + * @param resourceInstance resource instance to be modelled. + * @param disableValidation if set to {@code true}, then any model validation checks will be disabled. + * @return resource model initialized by the class or {@code null} if the + * class does not represent a resource. + */ + public static Resource from(Class resourceClass, Object resourceInstance, boolean disableValidation) { + final Builder builder = new IntrospectionModeller(resourceClass, resourceInstance, + disableValidation).createResourceBuilder(); return builder.isEmpty() ? null : builder.build(); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java index 8cb3601102..0e1f8e24c6 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java @@ -416,12 +416,7 @@ public Builder encodedParameters(boolean value) { * @return updated builder object. */ public Builder handledBy(Class handlerClass, Method method) { - this.handlerInstance = null; - - this.handlerClass = handlerClass; - this.definitionMethod = method; - - return this; + return handledBy(handlerClass, null, method); } /** @@ -432,11 +427,20 @@ public Builder handledBy(Class handlerClass, Method method) { * @return updated builder object. */ public Builder handledBy(Object handlerInstance, Method method) { - this.handlerClass = null; + return handledBy(null, handlerInstance, method); + } + /** + * Define a resource method handler binding. + * + * @param handlerInstance concrete resource method handler instance. + * @param method handling method. + * @return updated builder object. + */ + public Builder handledBy(Class handlerClass, Object handlerInstance, Method method) { + this.handlerClass = handlerClass; this.handlerInstance = handlerInstance; this.definitionMethod = method; - return this; } @@ -555,7 +559,7 @@ private Invocable createInvocable() { assert handlerClass != null || handlerInstance != null; final MethodHandler handler; - if (handlerClass != null) { + if (handlerClass != null && (handlerInstance == null || Resource.isAcceptable(handlerClass))) { handler = MethodHandler.create(handlerClass, encodedParams, handlerParameters); } else { // instance based handler = MethodHandler.create(handlerInstance, handlerParameters); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/ProxyResourceTest.java b/core-server/src/test/java/org/glassfish/jersey/server/ProxyResourceTest.java new file mode 100644 index 0000000000..9ec38f3082 --- /dev/null +++ b/core-server/src/test/java/org/glassfish/jersey/server/ProxyResourceTest.java @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import org.glassfish.jersey.server.model.Resource; +import org.junit.Test; + +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.assertEquals; + +/** + * Unit test for creating an application with interface resource and proxy instance + * via {@link Resource}'s programmatic API. + * + * @author DangCat (fan.shutian@zte.com.cn) + */ +public class ProxyResourceTest { + @Path("/srv1") + public interface ServiceOne { + @GET + String getName(); + + @PUT + @Path("{name}") + void setName(@PathParam("name") String name); + } + + public static class ServiceOneImpl implements ServiceOne { + private String name = null; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + } + + private ApplicationHandler createApplication() { + final ResourceConfig resourceConfig = new ResourceConfig(); + final ServiceOne serviceOne = new ServiceOneImpl(); + Object proxyService = Proxy.newProxyInstance(ServiceOneImpl.class.getClassLoader(), + new Class[]{ServiceOne.class}, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(serviceOne, args); + } + }); + resourceConfig.registerResources(Resource.from(ServiceOne.class, proxyService, false)); + return new ApplicationHandler(resourceConfig); + } + + @Test + public void testProxyResource() throws InterruptedException, ExecutionException { + ApplicationHandler application = createApplication(); + application.apply(RequestContextBuilder.from("/srv1/" + ServiceOne.class.getName(), + "PUT") + .build()); + Object result = application.apply(RequestContextBuilder.from("/srv1", + "GET") + .build()) + .get().getEntity(); + assertEquals(ServiceOne.class.getName(), result); + } +}