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

Commit eafaaa7

Browse files
Marek PotociarGerrit Code Review
authored andcommitted
Merge "JERSEY-2642: Create the new FormEntityValueFactory for URL-ENCODED entity"
2 parents 6df1cc4 + 0fde68f commit eafaaa7

File tree

2 files changed

+240
-1
lines changed

2 files changed

+240
-1
lines changed

core-server/src/main/java/org/glassfish/jersey/server/internal/inject/EntityParamValueFactoryProvider.java

Lines changed: 64 additions & 1 deletion
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-2015 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,13 +41,18 @@
4141

4242
import javax.ws.rs.BadRequestException;
4343
import javax.ws.rs.container.ContainerRequestContext;
44+
import javax.ws.rs.core.Form;
45+
import javax.ws.rs.core.MediaType;
46+
import javax.ws.rs.core.MultivaluedMap;
4447
import javax.ws.rs.core.Request;
4548
import javax.ws.rs.core.Response;
4649

4750
import javax.inject.Inject;
4851
import javax.inject.Singleton;
4952

53+
import org.glassfish.jersey.message.internal.MediaTypes;
5054
import org.glassfish.jersey.server.ContainerRequest;
55+
import org.glassfish.jersey.server.internal.InternalServerProperties;
5156
import org.glassfish.jersey.server.internal.LocalizationMessages;
5257
import org.glassfish.jersey.server.model.Parameter;
5358

@@ -104,8 +109,66 @@ public Object provide() {
104109
}
105110
}
106111

112+
private static class FormEntityValueFactory extends AbstractContainerRequestValueFactory<Object> {
113+
114+
private final MultivaluedParameterExtractor<?> extractor;
115+
116+
public FormEntityValueFactory(MultivaluedParameterExtractor<?> extractor) {
117+
this.extractor = extractor;
118+
}
119+
120+
@Override
121+
public Object provide() {
122+
final ContainerRequest request = getContainerRequest();
123+
124+
Form form = getCachedForm(request);
125+
if (form == null) {
126+
form = getForm(request);
127+
cacheForm(request, form);
128+
}
129+
130+
return form.asMap();
131+
}
132+
133+
private static Form getCachedForm(final ContainerRequest request) {
134+
return (Form) request.getProperty(InternalServerProperties.FORM_DECODED_PROPERTY);
135+
}
136+
137+
private void cacheForm(final ContainerRequest request, final Form form) {
138+
request.setProperty(InternalServerProperties.FORM_DECODED_PROPERTY, form);
139+
}
140+
141+
private Form getForm(final ContainerRequest request) {
142+
return getFormParameters(ensureValidRequest(request));
143+
}
144+
145+
private static ContainerRequest ensureValidRequest(final ContainerRequest request) throws IllegalStateException {
146+
if (request.getMethod().equals("GET")) {
147+
throw new IllegalStateException(LocalizationMessages.FORM_PARAM_METHOD_ERROR());
148+
}
149+
150+
if (!MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) {
151+
throw new IllegalStateException(LocalizationMessages.FORM_PARAM_CONTENT_TYPE_ERROR());
152+
}
153+
return request;
154+
}
155+
156+
private Form getFormParameters(ContainerRequest request) {
157+
if (MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) {
158+
request.bufferEntity();
159+
Form form = request.readEntity(Form.class);
160+
return (form == null ? new Form() : form);
161+
} else {
162+
return new Form();
163+
}
164+
}
165+
}
166+
107167
@Override
108168
protected Factory<?> createValueFactory(Parameter parameter) {
169+
if (MultivaluedMap.class.isAssignableFrom(parameter.getRawType())) {
170+
return new FormEntityValueFactory(get(parameter));
171+
}
109172
return new EntityValueFactory(parameter);
110173
}
111174
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 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+
package org.glassfish.jersey.tests.e2e.server;
41+
42+
import javax.ws.rs.Encoded;
43+
import javax.ws.rs.FormParam;
44+
import javax.ws.rs.POST;
45+
import javax.ws.rs.Path;
46+
import javax.ws.rs.client.Entity;
47+
import javax.ws.rs.core.Application;
48+
import javax.ws.rs.core.MediaType;
49+
import javax.ws.rs.core.MultivaluedMap;
50+
import javax.ws.rs.core.Response;
51+
52+
import org.glassfish.jersey.server.ResourceConfig;
53+
import org.glassfish.jersey.test.JerseyTest;
54+
55+
import org.junit.Test;
56+
import static org.junit.Assert.assertEquals;
57+
58+
/**
59+
* Tests that the MultivaluedMap injection does not close the request buffer and allows
60+
* to proceed other FormParam injections.
61+
*
62+
* @author Petr Bouda (petr.bouda at oracle.com)
63+
*/
64+
public class FormParamMultivaluedInjectionTest extends JerseyTest {
65+
66+
public static final String PREDEFINED_RESPONSE = "Hello George Javatar";
67+
68+
@Path("form")
69+
public static class FormResource {
70+
71+
@POST
72+
@Path("simple")
73+
public Response simple(MultivaluedMap<String, String> formParams,
74+
@FormParam("firstname") String firstname,
75+
@FormParam("lastname") String lastname) {
76+
assertEquals(2, formParams.size());
77+
assertEquals("George", formParams.get("firstname").get(0));
78+
assertEquals("Javatar", formParams.get("lastname").get(0));
79+
return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
80+
}
81+
82+
@POST
83+
@Path("nullable")
84+
public Response nullable(MultivaluedMap<String, String> formParams,
85+
@FormParam("firstname") String firstname,
86+
@FormParam("lastname") String lastname) {
87+
assertEquals(2, formParams.size());
88+
assertEquals(2, formParams.get("firstname").size());
89+
assertEquals("George", formParams.get("firstname").get(0));
90+
assertEquals("Javatar", formParams.get("lastname").get(0));
91+
return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
92+
}
93+
94+
@POST
95+
@Path("mixed")
96+
public Response mixed(@FormParam("firstname") String firstname,
97+
MultivaluedMap<String, String> formParams,
98+
@FormParam("lastname") String lastname) {
99+
assertEquals(2, formParams.size());
100+
assertEquals(2, formParams.get("firstname").size());
101+
assertEquals("George", formParams.get("firstname").get(0));
102+
assertEquals("Javatar", formParams.get("lastname").get(0));
103+
return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
104+
}
105+
106+
@POST
107+
@Path("encoded")
108+
public Response encoded(MultivaluedMap<String, String> formParams,
109+
@Encoded @FormParam("firstname") String firstname,
110+
@FormParam("lastname") String lastname) {
111+
assertEquals(2, formParams.size());
112+
assertEquals("George", formParams.get("firstname").get(0));
113+
assertEquals("Javatar", formParams.get("lastname").get(0));
114+
return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
115+
}
116+
117+
}
118+
119+
@Path("form-ext")
120+
public static class FormExtResource {
121+
122+
@Encoded @FormParam("firstname") String firstname;
123+
124+
@POST
125+
@Path("encoded")
126+
public Response encoded(MultivaluedMap<String, String> formParams,
127+
@FormParam("lastname") String lastname) {
128+
assertEquals(2, formParams.size());
129+
assertEquals("George", formParams.get("firstname").get(0));
130+
assertEquals("Javatar", formParams.get("lastname").get(0));
131+
return Response.status(Response.Status.OK).entity("Hello " + firstname + " " + lastname).build();
132+
}
133+
134+
}
135+
136+
@Override
137+
protected Application configure() {
138+
return new ResourceConfig(FormResource.class, FormExtResource.class);
139+
}
140+
141+
@Test
142+
public void testFormMultivaluedParam() {
143+
Response result = call("/form/simple", "firstname=George&lastname=Javatar");
144+
assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
145+
}
146+
147+
@Test
148+
public void testFormMultivaluedParamWithNull() {
149+
Response result = call("/form/nullable", "firstname=George&firstname&lastname=Javatar");
150+
assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
151+
}
152+
153+
@Test
154+
public void testFormMultivaluedParamMixedParamOrder() {
155+
Response result = call("/form/mixed", "firstname=George&firstname&lastname=Javatar");
156+
assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
157+
}
158+
159+
@Test
160+
public void testFormMultivaluedParamEncoded() {
161+
Response result = call("/form/encoded", "firstname=George&lastname=Javatar");
162+
assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
163+
}
164+
165+
@Test
166+
public void testFormMultivaluedParamExternalEncodedInjection() {
167+
Response result = call("/form-ext/encoded", "firstname=George&lastname=Javatar");
168+
assertEquals(PREDEFINED_RESPONSE, result.readEntity(String.class));
169+
}
170+
171+
private Response call(String path, String entity) {
172+
return target().path(path).request()
173+
.post(Entity.entity(entity, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
174+
}
175+
}
176+

0 commit comments

Comments
 (0)