This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new e920da53d7 CXF-9178: Run Jakarta RESTful Web Services 4.0 TCK. Fix
multipart test cases (#2844)
e920da53d7 is described below
commit e920da53d7538ed702746dec94ddae2aa82e3a01
Author: Andriy Redko <[email protected]>
AuthorDate: Fri Jan 30 09:12:08 2026 -0500
CXF-9178: Run Jakarta RESTful Web Services 4.0 TCK. Fix multipart test
cases (#2844)
---
.../org/apache/cxf/jaxrs/impl/EntityPartImpl.java | 6 +-
.../apache/cxf/jaxrs/utils/EntityPartUtils.java | 34 +-
.../org/apache/cxf/jaxrs/utils/InjectionUtils.java | 74 +++-
.../org/apache/cxf/jaxrs/utils/JAXRSUtils.java | 18 +-
.../apache/cxf/jaxrs/utils/InjectionUtilsTest.java | 2 +-
.../cxf/systest/jaxrs/JAXRSEntityPartTest.java | 25 +-
.../jaxrs/multipart/MultipartSupportTest.java | 385 +++++++++++++++++++++
tck/cxf-tck/pom.xml | 2 +
8 files changed, 492 insertions(+), 54 deletions(-)
diff --git
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/EntityPartImpl.java
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/EntityPartImpl.java
index bce0850ce0..88f054f5a5 100644
---
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/EntityPartImpl.java
+++
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/EntityPartImpl.java
@@ -20,6 +20,7 @@
package org.apache.cxf.jaxrs.impl;
import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
@@ -27,7 +28,6 @@ import java.io.PipedOutputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -91,8 +91,8 @@ public class EntityPartImpl implements EntityPart {
return (InputStream) content;
} else if (content instanceof byte[]) {
return new ByteArrayInputStream((byte[]) content);
- } else if (fileName != null && !fileName.isBlank()) {
- return Files.newInputStream(Path.of(fileName));
+ } else if (fileName != null && content instanceof File) {
+ return Files.newInputStream(((File) content).toPath());
} else {
return contentAsStream();
}
diff --git
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/EntityPartUtils.java
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/EntityPartUtils.java
index 08f7c2837b..6eeb8a13f1 100644
---
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/EntityPartUtils.java
+++
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/EntityPartUtils.java
@@ -19,16 +19,15 @@
package org.apache.cxf.jaxrs.utils;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.ext.Providers;
-import org.apache.cxf.attachment.AttachmentBoundaryDeserializer;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
@@ -50,14 +49,29 @@ final class EntityPartUtils {
return from(mc.getProviders(), AttachmentUtils.getMultipartBody(mc));
}
- public static EntityPart getEntityPart(final String value, final Message
message) {
+ public static EntityPart getEntityPart(final String key, final String
value, final Message message) {
final Providers providers = new ProvidersImpl(message);
- try (InputStream is = new
ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8))) {
- final AttachmentBoundaryDeserializer deserializer = new
AttachmentBoundaryDeserializer(message);
- final Attachment attachment = new
Attachment(deserializer.read(is), providers);
- return createFromAttachment(providers, attachment);
- } catch (IOException ex) {
- throw ExceptionUtils.toBadRequestException(null, null);
+ final MultipartBody body = (MultipartBody)
message.get(MultipartBody.INBOUND_MESSAGE_ATTACHMENTS);
+ if (body != null) {
+ final Attachment attachment = body.getAttachment(key);
+ if (attachment != null) {
+ final ContentDisposition cd =
attachment.getContentDisposition();
+ final String fileName = (cd != null) ? cd.getFilename() : null;
+
+ if (!StringUtils.isEmpty(fileName)) {
+ return new EntityPartImpl(providers, key, fileName, value,
String.class, null,
+ attachment.getHeaders(), attachment.getContentType());
+ } else {
+ return new EntityPartImpl(providers, key, null, value,
String.class, null, attachment.getHeaders(),
+ attachment.getContentType());
+ }
+ } else {
+ return new EntityPartImpl(providers, key, null, value,
String.class,
+ null, new MultivaluedHashMap<>(),
MediaType.TEXT_PLAIN_TYPE);
+ }
+ } else {
+ return new EntityPartImpl(providers, key, null, value,
String.class,
+ null, new MultivaluedHashMap<>(), MediaType.TEXT_PLAIN_TYPE);
}
}
diff --git
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
index 17e15d63ff..901d9de4e6 100644
---
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
+++
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java
@@ -19,6 +19,8 @@
package org.apache.cxf.jaxrs.utils;
+import java.io.InputStream;
+import java.io.StringReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
@@ -77,6 +79,7 @@ import org.apache.cxf.common.util.ProxyClassLoaderCache;
import org.apache.cxf.common.util.ReflectionUtil;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.io.ReaderInputStream;
import org.apache.cxf.jaxrs.ext.ContextProvider;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.apache.cxf.jaxrs.ext.ProtocolHeaders;
@@ -100,6 +103,7 @@ import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
+import org.jspecify.annotations.Nullable;
public final class InjectionUtils {
public static final Set<String> STANDARD_CONTEXT_CLASSES = new HashSet<>();
@@ -414,8 +418,12 @@ public final class InjectionUtils {
}
return null;
}
-
+
+ /**
+ * @deprecated please use {@link handleParameter} with key argument
+ */
@SuppressWarnings("unchecked")
+ @Deprecated
public static <T> T handleParameter(String value,
boolean decoded,
Class<T> pClass,
@@ -423,6 +431,20 @@ public final class InjectionUtils {
Annotation[] paramAnns,
ParameterType pType,
Message message) {
+ return handleParameter(null, value, decoded, pClass,
+ genericType, paramAnns, pType, message);
+ }
+
+ //CHECKSTYLE:OFF
+ @SuppressWarnings("unchecked")
+ public static <T> T handleParameter(@Nullable String key,
+ String value,
+ boolean decoded,
+ Class<T> pClass,
+ Type genericType,
+ Annotation[] paramAnns,
+ ParameterType pType,
+ Message message) {
if (value == null) {
return null;
}
@@ -525,7 +547,11 @@ public final class InjectionUtils {
}
if (EntityPart.class.isAssignableFrom(pClass) && message != null) {
- result = EntityPartUtils.getEntityPart(value, message);
+ result = EntityPartUtils.getEntityPart(key, value, message);
+ }
+
+ if (InputStream.class.isAssignableFrom(pClass) && message != null) {
+ result = new ReaderInputStream(new StringReader(value));
}
if (result == null) {
@@ -539,6 +565,7 @@ public final class InjectionUtils {
return null;
}
}
+ //CHECKSTYLE:ON
private static RuntimeException
createParamConversionException(ParameterType pType, Exception ex) {
//
@@ -741,14 +768,14 @@ public final class InjectionUtils {
for (MultivaluedMap<String, String> processedValues :
processedValuesList) {
if (InjectionUtils.isSupportedCollectionOrArray(type))
{
- Object appendValue =
InjectionUtils.injectIntoCollectionOrArray(type,
+ Object appendValue =
InjectionUtils.injectIntoCollectionOrArray(memberKey, type,
genericType,
paramAnns, processedValues,
isbean, true,
pType, message);
paramValue =
InjectionUtils.mergeCollectionsOrArrays(paramValue, appendValue,
genericType);
} else if (isSupportedMap(genericType)) {
- Object appendValue = injectIntoMap(
+ Object appendValue = injectIntoMap(memberKey,
genericType, paramAnns, processedValues, true,
pType, message);
paramValue = mergeMap(paramValue, appendValue);
@@ -756,7 +783,7 @@ public final class InjectionUtils {
paramValue = InjectionUtils.handleBean(type,
paramAnns, processedValues,
pType, message,
decoded);
} else {
- paramValue = InjectionUtils.handleParameter(
+ paramValue =
InjectionUtils.handleParameter(memberKey,
processedValues.values().iterator().next().get(0),
decoded, type, type, paramAnns, pType,
message);
}
@@ -787,7 +814,8 @@ public final class InjectionUtils {
return null;
}
- private static Object injectIntoMap(Type genericType,
+ private static Object injectIntoMap(String key,
+ Type genericType,
Annotation[] paramAnns,
MultivaluedMap<String, String>
processedValues,
boolean decoded,
@@ -805,7 +833,7 @@ public final class InjectionUtils {
for (Map.Entry<String, List<String>> processedValuesEntry :
processedValues.entrySet()) {
List<String> valuesList = processedValuesEntry.getValue();
for (String value : valuesList) {
- Object o = InjectionUtils.handleParameter(value,
+ Object o = InjectionUtils.handleParameter(key, value,
decoded, valueType, valueType,
paramAnns, pathParam, message);
theValues.add(convertStringToPrimitive(processedValuesEntry.getKey(), keyType),
o);
}
@@ -818,7 +846,7 @@ public final class InjectionUtils {
for (Map.Entry<String, List<String>> processedValuesEntry :
processedValues.entrySet()) {
List<String> valuesList = processedValuesEntry.getValue();
for (String value : valuesList) {
- Object o = InjectionUtils.handleParameter(value,
+ Object o = InjectionUtils.handleParameter(key, value,
decoded, valueType, valueType, paramAnns,
pathParam, message);
theValues.put(
convertStringToPrimitive(processedValuesEntry.getKey(),
keyType),
@@ -955,7 +983,7 @@ public final class InjectionUtils {
}
//CHECKSTYLE:OFF
- private static Object injectIntoCollectionOrArray(Class<?> rawType,
+ private static Object injectIntoCollectionOrArray(String key, Class<?>
rawType,
Type genericType,
Annotation[] paramAnns,
MultivaluedMap<String, String> values,
@@ -998,7 +1026,7 @@ public final class InjectionUtils {
List<String> valuesList = values.values().iterator().next();
valuesList = checkPathSegment(valuesList, realType, pathParam);
for (int ind = 0; ind < valuesList.size(); ind++) {
- Object o = InjectionUtils.handleParameter(valuesList.get(ind),
decoded,
+ Object o = InjectionUtils.handleParameter(key,
valuesList.get(ind), decoded,
realType, realGenericType, paramAnns,
pathParam, message);
addToCollectionValues(theValues, o, ind);
}
@@ -1038,9 +1066,29 @@ public final class InjectionUtils {
}
return newValues;
}
- //
+
+ /**
+ * @deprecated please use {@link createParameterObject} with key argument
+ */
+ @Deprecated
//CHECKSTYLE:OFF
public static Object createParameterObject(List<String> paramValues,
+ Class<?> paramType,
+ Type genericType,
+ Annotation[] paramAnns,
+ String defaultValue,
+ boolean decoded,
+ ParameterType pathParam,
+ Message message) {
+ return createParameterObject(null, paramValues, paramType,
genericType,
+ paramAnns, defaultValue, decoded, pathParam, message);
+ }
+ //CHECKSTYLE:ON
+
+ //
+ //CHECKSTYLE:OFF
+ public static Object createParameterObject(String key,
+ List<String> paramValues,
Class<?> paramType,
Type genericType,
Annotation[] paramAnns,
@@ -1070,7 +1118,7 @@ public final class InjectionUtils {
if (InjectionUtils.isSupportedCollectionOrArray(paramType)) {
MultivaluedMap<String, String> paramValuesMap = new
MetadataMap<>();
paramValuesMap.put("", paramValues);
- value = InjectionUtils.injectIntoCollectionOrArray(paramType,
genericType, paramAnns,
+ value = InjectionUtils.injectIntoCollectionOrArray(key, paramType,
genericType, paramAnns,
paramValuesMap, false,
decoded, pathParam, message);
} else {
String result = null;
@@ -1080,7 +1128,7 @@ public final class InjectionUtils {
: paramValues.get(0);
}
if (result != null) {
- value = InjectionUtils.handleParameter(result, decoded,
paramType, genericType,
+ value = InjectionUtils.handleParameter(key, result, decoded,
paramType, genericType,
paramAnns, pathParam,
message);
}
}
diff --git
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
index a2ca3d98f2..c89a2952d5 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
@@ -1162,7 +1162,8 @@ public final class JAXRSUtils {
return InjectionUtils.handleBean(pClass, paramAnns, params,
ParameterType.MATRIX, m, false);
}
List<String> values = params.get(key);
- return InjectionUtils.createParameterObject(values,
+ return InjectionUtils.createParameterObject(key,
+ values,
pClass,
genericType,
paramAnns,
@@ -1232,7 +1233,8 @@ public final class JAXRSUtils {
}
List<String> results = params.get(key);
- return InjectionUtils.createParameterObject(results,
+ return InjectionUtils.createParameterObject(key,
+ results,
pClass,
genericType,
paramAnns,
@@ -1260,7 +1262,8 @@ public final class JAXRSUtils {
if (values != null && values.isEmpty()) {
values = null;
}
- return InjectionUtils.createParameterObject(values,
+ return InjectionUtils.createParameterObject(header,
+ values,
pClass,
genericType,
paramAnns,
@@ -1290,7 +1293,8 @@ public final class JAXRSUtils {
String value = InjectionUtils.isSupportedCollectionOrArray(pClass)
&& InjectionUtils.getActualType(genericType) == Cookie.class
? c.toString() : c.getValue();
- return
InjectionUtils.createParameterObject(Collections.singletonList(value),
+ return InjectionUtils.createParameterObject(cookieName,
+
Collections.singletonList(value),
pClass,
genericType,
paramAnns,
@@ -1427,7 +1431,8 @@ public final class JAXRSUtils {
return InjectionUtils.handleBean(paramType, paramAnns, values,
ParameterType.PATH, m, decoded);
}
List<String> results = values.get(parameterName);
- return InjectionUtils.createParameterObject(results,
+ return InjectionUtils.createParameterObject(parameterName,
+ results,
paramType,
genericType,
paramAnns,
@@ -1453,7 +1458,8 @@ public final class JAXRSUtils {
if ("".equals(queryName)) {
return InjectionUtils.handleBean(paramType, paramAnns, queryMap,
ParameterType.QUERY, m, false);
}
- return InjectionUtils.createParameterObject(queryMap.get(queryName),
+ return InjectionUtils.createParameterObject(queryName,
+ queryMap.get(queryName),
paramType,
genericType,
paramAnns,
diff --git
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/InjectionUtilsTest.java
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/InjectionUtilsTest.java
index 1b5f07be2f..7d4a6df867 100644
---
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/InjectionUtilsTest.java
+++
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/InjectionUtilsTest.java
@@ -199,7 +199,7 @@ public class InjectionUtilsTest {
public void testJsr310DateExceptionHandling() {
Field field = CustomerDetailsWithAdapter.class.getDeclaredFields()[0];
Annotation[] paramAnns = field.getDeclaredAnnotations();
-
InjectionUtils.createParameterObject(Collections.singletonList("wrongDate"),
LocalDate.class,
+ InjectionUtils.createParameterObject(field.getName(),
Collections.singletonList("wrongDate"), LocalDate.class,
LocalDate.class, paramAnns, null, false, ParameterType.QUERY,
createMessage());
}
diff --git
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
index b536c69383..6506372b72 100644
---
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
+++
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
@@ -21,6 +21,7 @@ package org.apache.cxf.systest.jaxrs;
import java.io.File;
import java.io.InputStream;
+import java.io.SequenceInputStream;
import java.nio.file.Files;
import java.util.List;
@@ -107,19 +108,7 @@ public class JAXRSEntityPartTest extends
AbstractBusClientServerTestBase {
InputStream is2 =
getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/attachmentFormJson"))
{
- final EntityPart part1 = EntityPart
- .withName("bookXML")
- .content(is1)
- .mediaType(MediaType.APPLICATION_XML)
- .build();
-
- final EntityPart part2 = EntityPart
- .withName("gazetteer")
- .content(is2)
- .mediaType(MediaType.APPLICATION_JSON)
- .build();
-
- try (Response response = client.postCollection(List.of(part1,
part2), EntityPart.class)) {
+ try (Response response = client.post(new SequenceInputStream(is1,
is2))) {
assertThat(response.getStatus(), equalTo(200));
try (InputStream expected =
getClass().getResourceAsStream("resources/expected_add_book.txt")) {
@@ -144,14 +133,8 @@ public class JAXRSEntityPartTest extends
AbstractBusClientServerTestBase {
try (InputStream is =
getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/" +
resourceName)) {
-
- final EntityPart part = EntityPart
- .withName(name)
- .content(is)
- .mediaType(mt)
- .build();
-
- try (Response response = client.postCollection(List.of(part),
EntityPart.class)) {
+
+ try (Response response = client.post(is)) {
assertThat(response.getStatus(), equalTo(200));
try (InputStream expected =
getClass().getResourceAsStream("resources/expected_add_book.txt")) {
diff --git
a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/multipart/MultipartSupportTest.java
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/multipart/MultipartSupportTest.java
new file mode 100644
index 0000000000..0a6ab0ebea
--- /dev/null
+++
b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/multipart/MultipartSupportTest.java
@@ -0,0 +1,385 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.jaxrs.multipart;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.List;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.FormParam;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.EntityPart;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.apache.cxf.Bus;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.testutil.common.AbstractServerTestServerBase;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Adapted from ee.jakarta.tck.ws.rs.jaxrs31.ee.multipart.MultipartSupportIT
+ */
+public class MultipartSupportTest extends AbstractBusClientServerTestBase {
+ public static final String PORT = MultipartSupportServer.PORT;
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ assertTrue("server did not launch correctly",
launchServer(MultipartSupportServer.class, true));
+ }
+
+ public static final class MultipartSupportServer extends
AbstractServerTestServerBase {
+ public static final String PORT =
allocatePort(MultipartSupportServer.class);
+
+ @Override
+ protected Server createServer(Bus bus) throws Exception {
+ JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(TestResource.class);
+ sf.setAddress("http://localhost:" + PORT + "/");
+ return sf.create();
+ }
+
+ public static void main(String[] args) throws Exception {
+ new MultipartSupportServer().start();
+ }
+ }
+
+ private static InputStream xmlFile() {
+ final String xml = "<root>" + System.lineSeparator()
+ + " <mid attr1=\"value1\" attr2=\"value2\">" +
System.lineSeparator()
+ + " <inner attr3=\"value3\"/>" + System.lineSeparator()
+ + " </mid>" + System.lineSeparator()
+ + " <mid attr1=\"value4\" attr2=\"value5\">" +
System.lineSeparator()
+ + " <inner attr3=\"value6\"/>" + System.lineSeparator()
+ + " </mid>" + System.lineSeparator()
+ + "</root>";
+ return new ByteArrayInputStream(xml.getBytes());
+ }
+
+ @Test
+ public void basicTest() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("octet-stream")
+ .content("test
string".getBytes(StandardCharsets.UTF_8))
+ .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .build(),
+ EntityPart.withName("file")
+ .content("test file", xmlFile())
+ .mediaType(MediaType.APPLICATION_XML)
+ .build()
+ );
+
+ try (Response response = client.target("http://localhost:" + PORT
+ "/test/basicTest")
+ .request(MediaType.MULTIPART_FORM_DATA_TYPE)
+ .post(Entity.entity(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA))) {
+
+ Assert.assertEquals(200, response.getStatus());
+ final List<EntityPart> entityParts = response.readEntity(new
GenericType<>() {
+ });
+
+ if (entityParts.size() != 3) {
+ Assert.fail("Expected 3 entries, received " +
entityParts.size() + '.');
+ }
+
+ EntityPart part = find(entityParts, "received-string");
+ Assert.assertNotNull(part);
+ Assert.assertEquals("test string",
part.getContent(String.class));
+
+ // The javadoc for EntityPart.getContent(Class<T> type) states:
+ // "Subsequent invocations will result in an {@code
IllegalStateException}.
+ // Likewise this method will throw an {@code
IllegalStateException} if it is
+ // called after calling {@link #getContent} or {@link
#getContent(GenericType)}.
+ try {
+ part.getContent(String.class);
+ Assert.fail("IllegalStateException is expected when
getContent() is invoked more than once.");
+ } catch (IllegalStateException e) {
+ // expected exception
+ } catch (Throwable t) {
+ Assert.fail("Incorrect Throwable received: " + t);
+ }
+
+ part = find(entityParts, "received-file");
+ Assert.assertNotNull(part);
+ Assert.assertEquals("test file", part.getFileName().get());
+
Assert.assertTrue(part.getContent(String.class).contains("value6"));
+
+ part = find(entityParts, "added-input-stream");
+ Assert.assertNotNull(part);
+ Assert.assertEquals("Add part on return.",
part.getContent(String.class));
+
+ // Check headers. Should be 5: Content-Disposition,
Transfer-Encoding,
+ // Content-Type, and the 2 headers that were added.
+ if ((part.getHeaders() == null) || (part.getHeaders().size()
!= 5)) {
+ Assert.fail("Expected 4 headers, received " +
part.getHeaders().size());
+ }
+
+ Assert.assertEquals("Test1",
part.getHeaders().get("Header1").get(0));
+ Assert.assertEquals("Test2",
part.getHeaders().get("Header2").get(0));
+ }
+ }
+ }
+
+ @Test
+ public void multiFormParamTest() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("entity-part")
+ .content("test entity part")
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build(),
+ EntityPart.withName("string-part")
+ .content("test string")
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build(),
+ EntityPart.withName("input-stream-part")
+ .content("test input
stream".getBytes(StandardCharsets.UTF_8))
+ .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .build()
+ );
+
+ try (Response response = client.target("http://localhost:" + PORT
+ "/test/multi-form-param")
+ .request(MediaType.MULTIPART_FORM_DATA_TYPE)
+ .post(Entity.entity(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA))) {
+ Assert.assertEquals(200, response.getStatus());
+ final List<EntityPart> entityParts = response.readEntity(new
GenericType<>() { });
+ if (entityParts.size() != 3) {
+ Assert.fail("Expected 3 entries, received " +
entityParts.size() + '.');
+ }
+ verifyEntityPart(entityParts, "received-entity-part", "test
entity part");
+ verifyEntityPart(entityParts, "received-string", "test
string");
+ verifyEntityPart(entityParts, "received-input-stream", "test
input stream");
+ }
+ }
+ }
+
+ @Test
+ public void singleFormParamTest() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("entity-part")
+ .content("test entity part")
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build()
+ );
+
+ try (Response response = client.target("http://localhost:" + PORT
+ "/test/single-form-param")
+ .request(MediaType.MULTIPART_FORM_DATA_TYPE)
+ .post(Entity.entity(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA))) {
+ Assert.assertEquals(200, response.getStatus());
+ final List<EntityPart> entityParts = response.readEntity(new
GenericType<>() { });
+ if (entityParts.size() != 1) {
+ Assert.fail("Expected 1 entries, received " +
entityParts.size() + '.');
+ }
+ verifyEntityPart(entityParts, "received-entity-part", "test
entity part");
+ }
+ }
+ }
+
+ @Test
+ public void singleTest() throws Exception {
+ try (Client client = ClientBuilder.newClient()) {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("entity-part")
+ .content(new ByteArrayInputStream("test entity
part".getBytes(StandardCharsets.UTF_8)))
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build()
+ );
+
+ try (Response response = client.target("http://localhost:" + PORT
+ "/test/single-param")
+ .request(MediaType.MULTIPART_FORM_DATA_TYPE)
+ .post(Entity.entity(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA))) {
+ Assert.assertEquals(200, response.getStatus());
+ final List<EntityPart> entityParts = response.readEntity(new
GenericType<>() { });
+ if (entityParts.size() != 1) {
+ Assert.fail("Expected 1 entries, received " +
entityParts.size() + '.');
+ }
+ verifyEntityPart(entityParts, "received-entity-part", "test
entity part");
+ }
+ }
+ }
+
+ @Test
+ public void fileTest() throws Exception {
+ final String resource =
"/org/apache/cxf/systest/jaxrs/resources/add_book.txt";
+ try (InputStream is = getClass().getResourceAsStream(resource)) {
+ final byte[] content = is.readAllBytes();
+ try (Client client = ClientBuilder.newClient()) {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withFileName("file-part")
+ .content(new ByteArrayInputStream(content))
+ .mediaType(MediaType.APPLICATION_XML_TYPE)
+ .build()
+ );
+
+ try (Response response = client.target("http://localhost:" +
PORT + "/test/file-param")
+ .request(MediaType.MULTIPART_FORM_DATA_TYPE)
+ .post(Entity.entity(new GenericEntity<>(multipart) {
}, MediaType.MULTIPART_FORM_DATA))) {
+ Assert.assertEquals(200, response.getStatus());
+ final List<EntityPart> entityParts =
response.readEntity(new GenericType<>() { });
+ if (entityParts.size() != 1) {
+ Assert.fail("Expected 1 entries, received " +
entityParts.size() + '.');
+ }
+ verifyEntityPart(entityParts, "received-file-part", new
String(content, StandardCharsets.UTF_8));
+ }
+ }
+ }
+ }
+
+ private static void verifyEntityPart(final List<EntityPart> parts, final
String name, final String text)
+ throws IOException {
+ final EntityPart part = find(parts, name);
+ Assert.assertNotNull(part);
+ Assert.assertEquals(text, part.getContent(String.class));
+ }
+
+ private static String toString(final InputStream in) throws IOException {
+ // try-with-resources fails here due to a bug in the
+ //noinspection TryFinallyCanBeTryWithResources
+ try {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[32];
+ int len;
+ while ((len = in.read(buffer)) > 0) {
+ out.write(buffer, 0, len);
+ }
+ return out.toString(StandardCharsets.UTF_8);
+ } finally {
+ in.close();
+ }
+ }
+
+ private static EntityPart find(final Collection<EntityPart> parts, final
String name) {
+ for (EntityPart part : parts) {
+ if (name.equals(part.getName())) {
+ return part;
+ }
+ }
+ return null;
+ }
+
+ @Path("/test")
+ public static class TestResource {
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.MULTIPART_FORM_DATA)
+ @Path("/basicTest")
+ public Response basicTest(final List<EntityPart> parts) throws
IOException {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("received-string")
+ .content(find(parts,
"octet-stream").getContent(byte[].class))
+ .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .build(),
+ EntityPart.withName("received-file")
+ .content(find(parts, "file").getFileName().get(),
find(parts, "file").getContent())
+ .mediaType(MediaType.APPLICATION_XML)
+ .build(),
+ EntityPart.withName("added-input-stream")
+ .content(new ByteArrayInputStream("Add part on
return.".getBytes()))
+ .mediaType("text/asciidoc")
+ .header("Header1", "Test1")
+ .header("Header2", "Test2")
+ .build());
+ return Response.ok(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA).build();
+ }
+
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.MULTIPART_FORM_DATA)
+ @Path("/multi-form-param")
+ public Response multipleFormParamTest(@FormParam("string-part") final
String string,
+ @FormParam("entity-part") final EntityPart entityPart,
+ @FormParam("input-stream-part") final InputStream in) throws
IOException {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("received-entity-part")
+ .content(entityPart.getContent(String.class))
+ .mediaType(entityPart.getMediaType())
+ .fileName(entityPart.getFileName().orElse(null))
+ .build(),
+ EntityPart.withName("received-input-stream")
+
.content(MultipartSupportTest.toString(in).getBytes(StandardCharsets.UTF_8))
+ .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .build(),
+ EntityPart.withName("received-string")
+ .content(string)
+ .mediaType(MediaType.TEXT_PLAIN_TYPE)
+ .build());
+ return Response.ok(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA).build();
+ }
+
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.MULTIPART_FORM_DATA)
+ @Path("/single-form-param")
+ public Response singleFormParamTest(@FormParam("entity-part") final
EntityPart entityPart) throws IOException {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("received-entity-part")
+ .content(entityPart.getContent(String.class))
+ .mediaType(entityPart.getMediaType())
+ .fileName(entityPart.getFileName().orElse(null))
+ .build());
+ return Response.ok(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA).build();
+ }
+
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.MULTIPART_FORM_DATA)
+ @Path("/single-param")
+ public Response singleParamTest(final List<EntityPart> parts) throws
IOException {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withName("received-entity-part")
+ .content(find(parts,
"entity-part").getContent(byte[].class))
+ .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .build());
+ return Response.ok(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA).build();
+ }
+
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.MULTIPART_FORM_DATA)
+ @Path("/file-param")
+ public Response fileParamTest(@FormParam("file") final EntityPart
filePart) throws IOException {
+ final List<EntityPart> multipart = List.of(
+ EntityPart.withFileName("received-file-part")
+ .content(filePart.getContent(byte[].class))
+ .mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .build());
+ return Response.ok(new GenericEntity<>(multipart) { },
MediaType.MULTIPART_FORM_DATA).build();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tck/cxf-tck/pom.xml b/tck/cxf-tck/pom.xml
index bec5f3a504..8ebe8e21b4 100644
--- a/tck/cxf-tck/pom.xml
+++ b/tck/cxf-tck/pom.xml
@@ -538,6 +538,8 @@
<configuration>
<excludes>
<exclude>**/SeBootstrapIT.java</exclude>
+ <!-- Covered by
org.apache.cxf.systest.jaxrs.multipart.MultipartSupportTest -->
+ <exclude>**/MultipartSupportIT.java</exclude>
</excludes>
<dependenciesToScan>jakarta.ws.rs:${tck.artifactId}</dependenciesToScan>
<systemPropertyVariables>