Hello
We have a deployed set of JAX-RS endpoints which work fine under Jersey
and
include both GET and POST annotated methods.
We are looking to move from Jersey to CXF with this and @GET methods
appear to be fine. However @POST methods are presenting some problems.
(Note: We place annotations on the interface and not the implementation
classes directly - this works in BOTH cases - the WADL returned appears
correct)
We are also using POSTMAN for the test, so it generates the form/mime
boundaries automatically. I've also omitted the HTTP Headers and binary
content for brevity.
*The Jersey case: Relevant annotations in yellow: - these appear to be
Jersey-specific* (all others are standard JAX-RS annotations)
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
@Path("/ordermanagement/{CUSTID}/orders")
public Response addOrder(@FormDataParam("json") String json,
@FormDataParam("orderfile")
InputStream contentStream, @FormDataParam("fileInfo")
FormDataContentDisposition fileInfo, @Context UriInfo info,
@PathParam(AppConstants.CUSTID) String CUSTID,
@QueryParam(AppConstants.SOURCE_APP_ID_PARAM) String srcAppId,
@QueryParam(AppConstants.SOURCE_APP_FEATURE_ID_PARAM) String
srcAppFeatureId) throws RESTServiceException;
*Example of POSTMAN test that WORKS SUCCESSFULLY for this case*
POST
/services/core/orderprocess/ordermanagement/22334455/orders?srcAppId=orderService&srcAppFeatureId=creation
HTTP/1.1
Host: localhost:8080
### MOST HEADERS-REMOVED-FOR-CLARITY ###
Cache-Control: no-cache
Postman-Token: db5a119a-e885-4bfd-14ae-8bac119845bd
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="json"
{ "displayName": "OrderxTest", "originalFileName": "OrderxTest.pdf",
"fileName": "OrderxTest.pdf", "OrderumentType": "pdf",
"OrderumentStatus": "CLEAR", "attachmentType": "Company", "category":
"DRAFT", "subcategory": "DRAFT", "keywords": "", "notes": [],
"fileSize": 163361, "uploadState": "PROGRESS", "metadataState":
"INCOMPLETE", "uploadFile": {}, "nextOrder": "" }
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="orderfile";
filename="OrderxTest.pdf"
Content-Type: application/pdf
----WebKitFormBoundary7MA4YWxkTrZu0gW
### BINARY DATA STREAM REMOVED ###
*The POST is processed successfully and we get back a HTTP 202*
*The CXF case: Relevant annotations in yellow: - these appear to be
CXF-specific, everything is the same except changing what we believed were
the required annotation changes*
*@POST*
*@Consumes(MediaType.MULTIPART_FORM_DATA)*
*@Produces(MediaType.APPLICATION_JSON)*
*@Path("/ordermanagement/{CUSTID}/orders")*
*public Response addOrderument(@Multipart("json") String json,
@Multipart("orderfile") InputStream contentStream, @FormParam("fileInfo")
ContentDisposition fileInfo, @Context UriInfo info, @QueryParam("$filter")
String filter) throws RESTServiceException;*
*Example of POSTMAN test that FAILS for this case*
POST
/services/core/orderprocess/ordermanagement/22334455/orders?srcAppId=orderService&srcAppFeatureId=creation
HTTP/1.1
Host: localhost:8080
### MOST HEADERS-REMOVED-FOR-CLARITY ###
Cache-Control: no-cache
Postman-Token: db5a119a-e885-4bfd-14ae-8bac119845bd
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="json"
{ "displayName": "OrderxTest", "originalFileName": "OrderxTest.pdf",
"fileName": "OrderxTest.pdf", "OrderumentType": "pdf",
"OrderumentStatus": "CLEAR", "attachmentType": "Company", "category":
"DRAFT", "subcategory": "DRAFT", "keywords": "", "notes": [],
"fileSize": 163361, "uploadState": "PROGRESS", "metadataState":
"INCOMPLETE", "uploadFile": {}, "nextOrder": "" }
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="orderfile";
filename="OrderxTest.pdf"
Content-Type: application/pdf
----WebKitFormBoundary7MA4YWxkTrZu0gW
### BINARY DATA STREAM REMOVED ###
The call fails with the following stack trace clearly something involving
the "multi-part" element processing... I am assuming we have made an error
in using what were believed to be the CXF-equivalent annotations.
java.lang.RuntimeException: *Invalid URL encoding: not a valid digit
(radix
16): 80*
* org.apache.cxf.common.util.UrlUtils.digit16(UrlUtils.java:114)*
* org.apache.cxf.common.util.UrlUtils.urlDecode(UrlUtils.java:94)*
* org.apache.cxf.common.util.UrlUtils.urlDecode(UrlUtils.java:66)*
* org.apache.cxf.common.util.UrlUtils.urlDecode(UrlUtils.java:121)*
* org.apache.cxf.jaxrs.utils.HttpUtils.urlDecode(HttpUtils.java:94)*
*
org.apache.cxf.jaxrs.utils.FormUtils.populateMapFromMultipart(FormUtils.java:230)*
*
org.apache.cxf.jaxrs.utils.JAXRSUtils.processFormParam(JAXRSUtils.java:956)*
*
org.apache.cxf.jaxrs.utils.JAXRSUtils.createHttpParameterValue(JAXRSUtils.java:877)*
*
org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:837)*
*
org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:787)*
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:212)
org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:77)
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:251)
org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:171)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:293)
org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:212)
javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
I did try changing the
@FormParam("fileInfo") ContentDisposition fileInfo part of the annotated
method to
@Multipart("fileInfo") ContentDisposition fileInfo
and this time the error was quite different, appears the MIME boundary is
confusing things ... or we clearly are not using the correct annotations
on
this:
Oct 25, 2015 9:10:08 AM
org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils getMultipart
WARNING: No multipart with content id fileInfo found, request content type
: multipart/form-data;boundary=----WebKitFormBoundarysJRabRa4EZMpZwwB
Oct 25, 2015 9:10:08 AM
org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: javax.ws.rs.BadRequestException: HTTP 400 Bad Request
at org.apache.cxf.jaxrs.utils.SpecExceptions.toBadRequestException(
SpecExceptions.java:84)
at org.apache.cxf.jaxrs.utils.ExceptionUtils.toBadRequestException(
ExceptionUtils.java:114)
at org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils.getMultipart(
AttachmentUtils.java:143)
at org.apache.cxf.jaxrs.provider.MultipartProvider.readFrom(
MultipartProvider.java:169)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(
JAXRSUtils.java:1340)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(
JAXRSUtils.java:1291)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(
JAXRSUtils.java:824)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(
JAXRSUtils.java:787)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(
JAXRSInInterceptor.java:212)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(
JAXRSInInterceptor.java:77)
Trying to understand how, if possible, to only change the annotations on
the method signature and still have the POST operation work under CXF. We
have managed to have the GET operations working fine, but there are far
less differences the GET operations are using mostly stack independent and
rely on standard JAX-RS annotations anyway.
Any insight from CXF experts would be appreciated...
Thanks
Mark