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