Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 3d218c8

Browse files
japodGerrit Code Review
authored andcommitted
Merge "JERSEY-2541: Generic types are missing for EJB Resources added an e2e test case that simulates the original issue added regression test into the jersey-ejb example application"
2 parents 60f3fa3 + d17b4a2 commit 3d218c8

File tree

4 files changed

+242
-10
lines changed

4 files changed

+242
-10
lines changed

core-server/src/main/java/org/glassfish/jersey/server/model/Invocable.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2011-2013 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2011-2014 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -41,6 +41,7 @@
4141

4242
import java.lang.reflect.Method;
4343
import java.lang.reflect.Type;
44+
import java.lang.reflect.ParameterizedType;
4445
import java.util.Arrays;
4546
import java.util.Collections;
4647
import java.util.List;
@@ -206,13 +207,30 @@ private Invocable(MethodHandler handler, Method definitionMethod, Method handlin
206207
.findOverridingMethodOnClass(handler.getHandlerClass(), definitionMethod) : handlingMethod;
207208

208209
final Class<?> handlerClass = handler.getHandlerClass();
209-
final ClassTypePair ctPair = ReflectionHelper.resolveGenericType(
210-
handlerClass,
211-
this.handlingMethod.getDeclaringClass(),
212-
this.handlingMethod.getReturnType(),
213-
this.handlingMethod.getGenericReturnType());
214-
this.rawResponseType = ctPair.rawClass();
215-
this.responseType = ctPair.type();
210+
final Class<?> definitionClass = definitionMethod.getDeclaringClass();
211+
final ClassTypePair handlingCtPair = ReflectionHelper.resolveGenericType(
212+
handlerClass,
213+
this.handlingMethod.getDeclaringClass(),
214+
this.handlingMethod.getReturnType(),
215+
this.handlingMethod.getGenericReturnType());
216+
217+
// here we need to find types also for definition method. Definition method is in most
218+
// cases used for parent methods (for example for interface method of resource class). But here we
219+
// consider also situation when resource is a proxy (for example proxy of EJB) and definition
220+
// method is the original method and handling method is method on proxy. So, we try to find generic
221+
// type in the original class using definition method.
222+
final ClassTypePair definitionCtPair = ReflectionHelper.resolveGenericType(
223+
definitionClass,
224+
this.definitionMethod.getDeclaringClass(),
225+
this.definitionMethod.getReturnType(),
226+
this.definitionMethod.getGenericReturnType());
227+
this.rawResponseType = handlingCtPair.rawClass();
228+
final boolean handlerReturnTypeIsParameterized = handlingCtPair.type() instanceof ParameterizedType;
229+
final boolean definitionReturnTypeIsParameterized = definitionCtPair.type() instanceof ParameterizedType;
230+
this.responseType =
231+
(handlingCtPair.rawClass() == definitionCtPair.rawClass()
232+
&& definitionReturnTypeIsParameterized && !handlerReturnTypeIsParameterized)
233+
? definitionCtPair.type() : handlingCtPair.type();
216234
if (routingResponseType == null) {
217235
this.routingResponseType = responseType;
218236
this.rawRoutingResponseType = rawResponseType;

core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,11 @@ public static final class Builder {
152152
// Invocable
153153
private Class<?> handlerClass;
154154
private Object handlerInstance;
155+
156+
// method (can be also interface method). Specific method to execute is defined by handlingMethod
155157
private Method definitionMethod;
156158

159+
// this can be either equal to definitionMethod or child of definitionMethod
157160
private Method handlingMethod;
158161
private boolean encodedParams;
159162
private Type routingResponseType;

examples/jersey-ejb/src/test/java/org/glassfish/jersey/examples/jersey_ejb/test/MessageBoardTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33
*
4-
* Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
4+
* Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved.
55
*
66
* The contents of this file are subject to the terms of either the GNU
77
* General Public License Version 2 only ("GPL") or the Common Development
@@ -63,7 +63,7 @@
6363
*
6464
* To test the app, mvn clean package and asadmin deploy target/jersey-ejb
6565
* and then run the tests using extenrnal test container factory:
66-
* mvn -DskipTests=false test
66+
* mvn -Prun-external-tests test
6767
*
6868
* @author Pavel Bucek (pavel.bucek at oracle.com)
6969
*/
@@ -79,6 +79,18 @@ protected URI getBaseUri() {
7979
return UriBuilder.fromUri(super.getBaseUri()).path("jersey-ejb").build();
8080
}
8181

82+
/**
83+
* Regression test for JERSEY-2541.
84+
*/
85+
@Test
86+
public void testListMessages() {
87+
Response response = target().path("app/messages").request(MediaType.TEXT_HTML).get();
88+
89+
assertEquals(
90+
String.format("Response status should be 200. Current value is %d.", response.getStatus()),
91+
200, response.getStatus());
92+
}
93+
8294
@Test
8395
public void testAddMessage() {
8496

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package org.glassfish.jersey.tests.e2e.entity;
42+
43+
import java.io.IOException;
44+
import java.io.OutputStream;
45+
46+
import java.lang.annotation.Annotation;
47+
import java.lang.reflect.ParameterizedType;
48+
import java.lang.reflect.Type;
49+
import java.lang.reflect.Proxy;
50+
import java.lang.reflect.InvocationHandler;
51+
import java.lang.reflect.Method;
52+
53+
import java.util.List;
54+
import java.util.LinkedList;
55+
56+
import javax.ws.rs.core.Application;
57+
import javax.ws.rs.core.MediaType;
58+
import javax.ws.rs.core.MultivaluedMap;
59+
import javax.ws.rs.core.Response;
60+
import javax.ws.rs.GET;
61+
import javax.ws.rs.Path;
62+
import javax.ws.rs.ext.Provider;
63+
import javax.ws.rs.ext.MessageBodyWriter;
64+
import javax.ws.rs.WebApplicationException;
65+
66+
import org.glassfish.jersey.server.ResourceConfig;
67+
import org.glassfish.jersey.test.JerseyTest;
68+
69+
import org.junit.Test;
70+
71+
import static org.junit.Assert.assertEquals;
72+
73+
/**
74+
* Reproducer for JERSEY-2541.
75+
*
76+
* Make sure that type parameter will be retained for resource method
77+
* of a sub-resource locator that returns a dynamic proxy.
78+
* This test should cover also the EJB case, as the common cause
79+
* is missing type parameter.
80+
*
81+
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
82+
*/
83+
public class SubResourceDynamicProxyTest extends JerseyTest {
84+
85+
@Override
86+
public Application configure() {
87+
return new ResourceConfig(ListProvider.class, RootResource.class);
88+
}
89+
90+
/**
91+
* Sub-resource interface so that it is easy to make a dynamic proxy
92+
* with the standard Java reflection API.
93+
*/
94+
public static interface SubResource {
95+
96+
@GET
97+
public List<Foo> getGreeting();
98+
}
99+
100+
/**
101+
* Helper type to be used as a type parameter.
102+
*/
103+
public static class Foo {
104+
}
105+
106+
/**
107+
* Message body writer that uses to check presence of type parameter in the provided entity type.
108+
* If no type parameter is found the provider refuses to process corresponding entity,
109+
* which would lead to an error.
110+
*/
111+
@Provider
112+
public static class ListProvider implements MessageBodyWriter<List<Foo>> {
113+
114+
/**
115+
* This is the data that would be written to the response body by the {@link ListProvider} bellow.
116+
*/
117+
final static String CHECK_DATA = "ensure this one makes it to the client";
118+
119+
/**
120+
* We need to work with a non-null entity here so that the worker could do it's job.
121+
*/
122+
final static LinkedList<Foo> TEST_ENTITY = new LinkedList<Foo>() {
123+
{
124+
add(new Foo());
125+
}
126+
};
127+
128+
@Override
129+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
130+
if (!(genericType instanceof ParameterizedType)) {
131+
return false;
132+
}
133+
134+
final ParameterizedType pt = (ParameterizedType) genericType;
135+
136+
if (pt.getActualTypeArguments().length > 1) {
137+
return false;
138+
}
139+
140+
if (!(pt.getActualTypeArguments()[0] instanceof Class)) {
141+
return false;
142+
}
143+
144+
final Class listClass = (Class) pt.getActualTypeArguments()[0];
145+
return listClass == Foo.class;
146+
}
147+
148+
@Override
149+
public long getSize(List<Foo> t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
150+
return -1;
151+
}
152+
153+
@Override
154+
public void writeTo(List<Foo> t, Class<?> type, Type genericType,
155+
Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
156+
OutputStream entityStream) throws IOException, WebApplicationException {
157+
158+
assertEquals(t, TEST_ENTITY);
159+
entityStream.write(CHECK_DATA.getBytes());
160+
}
161+
}
162+
163+
@Path("root")
164+
public static class RootResource {
165+
166+
/**
167+
* Sub-resource locator is used here, so that resource model will be built
168+
* at runtime, when the actual handler is the dynamic proxy.
169+
*
170+
* @return dynamic proxy for the sub-resource
171+
*/
172+
@Path("sub")
173+
public SubResource getSubresource() {
174+
return (SubResource) Proxy.newProxyInstance(
175+
RootResource.class.getClassLoader(),
176+
new Class<?>[]{SubResource.class},
177+
new InvocationHandler() {
178+
179+
@Override
180+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
181+
return ListProvider.TEST_ENTITY;
182+
}
183+
});
184+
}
185+
}
186+
187+
/**
188+
* Make sure the request is processed without errors, and the data
189+
* written by the {@link ListProvider} is returned back to the client.
190+
*/
191+
@Test
192+
public void testSubResourceProxy() {
193+
194+
final Response response = target("/root/sub").request().get();
195+
196+
assertEquals(200, response.getStatus());
197+
assertEquals(ListProvider.CHECK_DATA, response.readEntity(String.class));
198+
}
199+
}

0 commit comments

Comments
 (0)