Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow having multiple annotations for multipart endpoint with @FormDataParam in any order #5639

Merged
merged 3 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 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
Expand All @@ -20,6 +20,8 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -232,7 +234,13 @@ public FormDataParamValueProvider(Parameter parameter, MultivaluedParameterExtra
@Override
public Object apply(ContainerRequest request) {
// Return the field value for the field specified by the sourceName property.
final List<FormDataBodyPart> parts = getEntity(request).getFields(parameter.getSourceName());
final String sourceName = parameter.getAnnotations().length == 1
? parameter.getSourceName()
: Arrays.stream(parameter.getAnnotations())
.filter(ann -> FormDataParam.class.isInstance(ann))
.map(ann -> FormDataParam.class.cast(ann))
.findFirst().get().value();
final List<FormDataBodyPart> parts = getEntity(request).getFields(sourceName);

final FormDataBodyPart part = parts != null ? parts.get(0) : null;
final MediaType mediaType = part != null ? part.getMediaType() : MediaType.TEXT_PLAIN_TYPE;
Expand Down Expand Up @@ -357,34 +365,38 @@ public FormDataParamValueParamProvider(Provider<MultivaluedParameterExtractorPro
} else {
return null;
}
} else if (parameter.getSourceAnnotation().annotationType() == FormDataParam.class) {
final String paramName = parameter.getSourceName();
if (paramName == null || paramName.isEmpty()) {
// Invalid query parameter name
return null;
}

if (Collection.class == rawType || List.class == rawType) {
final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0);
} else {
for (Annotation sourceAnnotation : parameter.getAnnotations()) {
if (sourceAnnotation.annotationType() == FormDataParam.class) {
final String paramName = ((FormDataParam) sourceAnnotation).value(); // sourceName refers to the last anno
if (paramName == null || paramName.isEmpty()) {
// Invalid query parameter name
return null;
}

if (FormDataBodyPart.class == clazz) {
// Return a collection of form data body part.
return new ListFormDataBodyPartValueProvider(paramName);
} else if (FormDataContentDisposition.class == clazz) {
// Return a collection of form data content disposition.
return new ListFormDataContentDispositionProvider(paramName);
} else {
// Return a collection of specific type.
return new FormDataParamValueProvider(parameter, get(parameter));
if (Collection.class == rawType || List.class == rawType) {
final Class clazz = ReflectionHelper.getGenericTypeArgumentClasses(parameter.getType()).get(0);

if (FormDataBodyPart.class == clazz) {
// Return a collection of form data body part.
return new ListFormDataBodyPartValueProvider(paramName);
} else if (FormDataContentDisposition.class == clazz) {
// Return a collection of form data content disposition.
return new ListFormDataContentDispositionProvider(paramName);
} else {
// Return a collection of specific type.
return new FormDataParamValueProvider(parameter, get(parameter));
}
} else if (FormDataBodyPart.class == rawType) {
return new FormDataBodyPartProvider(paramName);
} else if (FormDataContentDisposition.class == rawType) {
return new FormDataContentDispositionProvider(paramName);
} else if (File.class == rawType) {
return new FileProvider(paramName);
} else {
return new FormDataParamValueProvider(parameter, get(parameter));
}
}
} else if (FormDataBodyPart.class == rawType) {
return new FormDataBodyPartProvider(paramName);
} else if (FormDataContentDisposition.class == rawType) {
return new FormDataContentDispositionProvider(paramName);
} else if (File.class == rawType) {
return new FileProvider(paramName);
} else {
return new FormDataParamValueProvider(parameter, get(parameter));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2024 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.media.multipart.internal;

import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.message.internal.ReaderWriter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.ParamQualifier;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.jupiter.api.Test;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class OrderParamTest extends JerseyTest {
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@ParamQualifier
public static @interface AnnoWithValue {
String value() default "";
}

@Path("/order")
public static class OrderTestResource {
@POST
@Path("/dataAfter")
@Consumes(value = MediaType.MULTIPART_FORM_DATA)
public String orderBefore(@FormDataParam("file") @AnnoWithValue("xxx") InputStream inputStream) throws IOException {
return ReaderWriter.readFromAsString(inputStream, MediaType.TEXT_PLAIN_TYPE);
}

@POST
@Path("/dataBefore")
@Consumes(value = MediaType.MULTIPART_FORM_DATA)
public String orderAfter(@AnnoWithValue("zzz") @FormDataParam("file") InputStream inputStream) throws IOException {
return ReaderWriter.readFromAsString(inputStream, MediaType.TEXT_PLAIN_TYPE);
}
}

@Override
protected Application configure() {
return new ResourceConfig(OrderTestResource.class).register(MultiPartFeature.class);
}

@Test
public void testOrder() {
final String MSG = "Hello";
FormDataMultiPart multiPart = new FormDataMultiPart();
multiPart.field("file", MSG, MediaType.TEXT_PLAIN_TYPE);
try (Response response = target("order")
.register(MultiPartFeature.class)
.path("dataBefore").request()
.post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
assertEquals(200, response.getStatus());
assertEquals(MSG, response.readEntity(String.class));
}

try (Response response = target("order")
.register(MultiPartFeature.class)
.path("dataAfter").request()
.post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE))) {
assertEquals(200, response.getStatus());
assertEquals(MSG, response.readEntity(String.class));
}
}
}