Hi Jean,
Thank you for the issue. As per my understanding, the deprecation
applies to multipart/form-data only, not others. Thank you.
Best Regards,
Andriy Redko
> Does this also apply to mutlipart/mixed and multipart/related content
> types?
> Anyway I created https://issues.apache.org/jira/browse/CXF-9138.
> Regards,
> J.P.
> -----Oorspronkelijk bericht-----
> Van: Andriy Redko <[email protected]>
> Verzonden: dinsdag 20 mei 2025 2:28
> Aan: Jean Pierre URKENS <[email protected]>;
> [email protected]
> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
> Hi Jean,
> I believe the rfc-7578 [1] deprecates Content-Transfer-Encoding (section
> 4.7), so
> it should be safe to remove it. Please feel free to file an issue [2], it
> should be an
> easy fix. Thank you!
> [1] https://www.rfc-editor.org/rfc/rfc7578
> [2] https://issues.apache.org/jira/browse/
> Best Regards,
> Andriy Redko
>> Hi andriy,
>> Indeed after omitting the Attachment::id property, which dropped the
>> Content-ID header, it worked.
>> However I couldn't get rid of the Content-Transfer-Encoding header, but
>> the server seems to have no problem with it.
>> Below the API method my client uses:
>> public Response createMessage(String xCorrelationId,String
>> idempotencyKey,MessageToSend messageToSend,DataSource upfile1,DataSource
>> upfile2,DataSource upfile3)
>> throws GeneralSecurityException, AuthorizationException {
>> //Create a multipartbody
>> List<Attachment> attachments = new ArrayList<>();
>> attachments.add(new AttachmentBuilder()
>> .object(messageToSend)
>> .contentDisposition(new
>> ContentDisposition("form-data; name=\"messageToSend\""))
>> .mediaType("application/json")
>> .build());
>> if (upfile1 != null) {
>> attachments.add(new AttachmentBuilder()
>> .dataHandler(new
>> DataHandler(upfile1))
>> .contentDisposition(new
>> ContentDisposition("form-data; name=\"upfile1\";
>> filename=\""+upfile1.getName()+"\""))
>> .build());
>> }
>> if (upfile2 != null) {
>> attachments.add(new AttachmentBuilder()
>> .dataHandler(new
>> DataHandler(upfile2))
>> .contentDisposition(new
>> ContentDisposition("form-data; name=\"upfile2\";
>> filename=\""+upfile2.getName()+"\""))
>> .build());
>> }
>> if (upfile3 != null) {
>> attachments.add(new AttachmentBuilder()
>> .dataHandler(new
>> DataHandler(upfile3))
>> .contentDisposition(new
>> ContentDisposition("form-data; name=\"upfile3\";
>> filename=\""+upfile3.getName()+"\""))
>> .build());
>> }
>> MultipartBody body = new
>> MultipartBody(attachments,MediaType.MULTIPART_FORM_DATA_TYPE,false);
>> //Authorize API client
>> Client client = WebClient.client(getApiProxy());
> authorizationHandler.authorize(client,resourceClientId,consumerClientId,Ma
>> gdadocService.MAGDADOC_SCOPES,consumerPrivKey);
>> WebClient wc = WebClient.fromClient(client,true)
>> .header("x-correlation-id",
>> xCorrelationId)
>> .header("Idempotency-Key",
> idempotencyKey)
>> .path("/messages")
> .accept(MediaType.APPLICATION_JSON_TYPE);
>> return
>> wc.post(Entity.entity(body,MediaType.MULTIPART_FORM_DATA_TYPE));
>> }
>> -----Oorspronkelijk bericht-----
>> Van: Andriy Redko <[email protected]>
>> Verzonden: maandag 19 mei 2025 17:06
>> Aan: Jean Pierre URKENS <[email protected]>;
>> [email protected]
>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>> Hi Jean,
>> Looked into it, so the Content-ID comes from Attachment::id property,
> the
>> good news -
>> this property is not required and could be omitted. Once omitted, the
>> Content-ID
>> header will not be sent. I am wondering how do you construct
> MultipartBody
>> /Attachment
>> instance that is being submitted to the service? Thank you.
>> Best Regards,
>> Andriy Redko
>>> Hi Andriy,
>>> Indeed, according to https://www.rfc-editor.org/rfc/rfc7578 -
>> section-4.8
>>> only the headers Content-Type, Content-Disposition, and
>>> Content-Transfer-Encoding (in specific cases) are supported.
>>> Actually (cf. https://www.rfc-editor.org/rfc/rfc7578#section-4.7) the
>> use
>>> of Content-Transfer-Encoding is deprecated. It is not clear to me where
>>> this header is set in CXF when constructing/streaming the parts of the
>>> multipart/formdata.
>>> Regards,
>>> J.P. Urkens
>>> -----Oorspronkelijk bericht-----
>>> Van: Andriy Redko <[email protected]>
>>> Verzonden: zaterdag 17 mei 2025 3:45
>>> Aan: Jean Pierre URKENS <[email protected]>;
>>> [email protected]
>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>> Hi Jean,
>>> Ah interesting, I briefly checked [1], [2] and indeed Content-ID is
>>> not mentioned there. I will try to look over the weekend what is
>>> happening,
>>> thank you.
>>> [1] https://www.rfc-editor.org/rfc/rfc2388.html
>>> [2] https://www.rfc-editor.org/rfc/rfc7578
>>> Best Regards,
>>> Andriy Redko
>>>> Hi Andriy,
>>>> It seems that the reason is the content-id header in the different
>>>> multiparts.
>>>> When I get the MessageToSend object passed along with 'Content-ID:
>>> <null>'
>>>> thus having the header like e.g.:
>>>> --uuid:0b5e2920-24d8-44fb-ac8f-672731573a3a
>>>> Content-Type: application/json
>>>> Content-Transfer-Encoding: binary
>>>> Content-ID: <null>
>>>> Content-Disposition: form-data; name="messageToSend"
>>>> then it works.
>>>> -----Oorspronkelijk bericht-----
>>>> Van: Andriy Redko <[email protected]>
>>>> Verzonden: vrijdag 16 mei 2025 3:41
>>>> Aan: Jean Pierre URKENS <[email protected]>;
>>>> [email protected]
>>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>>> Hi Jean,
>>>> On the first glance (comparing the example request and the real
> request
>>>> you are sending), I see no issues: there is one attachment in both of
>>> them
>>>> "upfile1". It is difficult to say what the cause is, but would you be
>>> able
>>>> to debug it by sending the POST request manually (Postman or even
>> curl)?
>>>> (if that is possible). Since you have both payloads (+ headers), we
>>> could
>>>> try to pinpoint the problematic part(s). What do you think?
>>>> Thank you.
>>>> Best Regards,
>>>> Andriy Redko
>>>>> Hi Andriy,
>>>>> Now that I am starting to test with the test server of the service
>>>>> provider its reporting me an '400 BAD REQUEST' with the message:
>>>>> "Number of attachments specified is different from the number
>>> of
>>>>> provided MultiPart files"
>>>>> It appears to me that the server is expecting 3 attachments
> (multipart
>>>>> files) and one object (messagetoSend).
>>>>> However, looking at the
>>> org.apache.cxf.jaxrs.ext.multipart.MultipartBody
>>>>> class, I can only add attachments to the multipartbody.
>>>>> It is not clear to me:
>>>>> - what exactly the difference is between what the server
>>> expects
>>>>> and what I send (see below)
>>>>> - how I can/should combine objects and files together in one
>>>>> MultipartBody of content-type mutlipart/form-data
>>>>> I am new to multipart messages, especially when it regards different
>>>>> content parts having different content-types, so any clarification
>>>>> would be helpful.
>>>>> Regards,
>>>>> J.P. Urkens
>>>>> The service provider provides following example as a valid request:
>>>>> POST /api/v1/messages/messages
>>>>> Authorization: Bearer oPilB4*************
>>>>> X-Correlation-ID: 7cb999b8-5e2a-450a-ae30-c2be9c1ba01c
>>>>> idempotency-key: fc1deb23-3869-4ccd-8afc-f0e40ad2b751
>>>>> Accept: */*
>>>>> Content-Type: multipart/form-data;
>>>>> boundary=--------------------------451703456417497490651597
>>>>> ----------------------------451703456417497490651597
>>>>> Content-Disposition: form-data; name="messageToSend"
>>>>> {
>>>>> "delivery": "EBOX",
>>>>> "eboxDeliveryData": {
>>>>> "recipient": {
>>>>> "eboxType": "CITIZEN",
>>>>> "eboxIdValue": "92042816483"
>>>>> },
>>>>> "subject": {
>>>>> "nl": "Onderwerp van het bericht",
>>>>> "fr": "Sujet du message",
>>>>> "de": "Test onderwerp in DE"
>>>>> },
>>>>> "messageTypeId":
>>> "139b65e2-12cf-4ef4-9023-641d0637f294",
>>>>> "senderOrganizationId": "0316380841",
>>>>> "senderApplicationId":
>>>>> "7d96bab6-e365-4c62-804a-aca5e07e2f57",
>>>>> "registeredMail": false,
>>>>> "bodyMainContent": false,
>>>>> "replyAuthorized": false,
>>>>> "attachments": [{
>>>>> "httpPartName": "upfile1",
>>>>> "mainContent": true,
>>>>> "attachmentSigned": false
>>>>> }]
>>>>> }
>>>>> }
>>>>> ----------------------------451703456417497490651597
>>>>> Content-Disposition: form-data; name="upfile1";
>>>>> filename="dummy.pdf"
>>>>> <dummy.pdf>
>>>>> ----------------------------451703456417497490651597--
>>>>> While my code is sending something like:
>>>>> [MAGDADOC] 2025-05-06 09:25:28,222 [main] INFO $--$
>>>>> (org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) - REQ_OUT
>>>>> Address:
>>>>> https://iv.api.tni-vlaanderen.be/api/v1/messages/messages
>>>>> HttpMethod: POST
>>>>> Content-Type: multipart/form-data;
>>>>> boundary="uuid:eb20df03-1120-4e02-b5c5-cb2f050ea975"
>>>>> ExchangeId: 0e03de2a-b3a8-41cb-98e2-7d3771e5dd7e
>>>>> Headers: {Authorization=Bearer
>>>>> 5XDR-xWf77BP5Pq3Xyt4Q5HWhzdAEu2Pmrz1zhzjMsM, Accept=application/json,
>>>>> Idempotency-Key=18ae8f19-9feb-4ff4-9ec3-52995964b90d,
>>>>> x-correlation-id=42623224-2755-45b2-abf5-5631ed247324}
>>>>> Payload:
>>>>> --uuid:eb20df03-1120-4e02-b5c5-cb2f050ea975
>>>>> Content-Type: application/json
>>>>> Content-Transfer-Encoding: binary
>>>>> Content-ID: <messageToSend>
>>>>> Content-Disposition: form-data; name="messageToSend";
>>>>> {
>>>>> "delivery" : "EBOX",
>>>>> "eboxDeliveryData" : {
>>>>> "recipient" : {
>>>>> "eboxType" : "ENTERPRISE",
>>>>> "enterpriseNumber" : "0887693124",
>>>>> "eboxIdValue" : "0887693124"
>>>>> },
>>>>> "subject" : {
>>>>> "nl" : "ProjectOnontvankelijkMail Project:
> 2025-EP-0001"
>>>>> },
>>>>> "registeredMail" : false,
>>>>> "attachments" : [ {
>>>>> "httpPartName" : "upfile1",
>>>>> "mainContent" : true,
>>>>> "attachmentSigned" : false
>>>>> }],
>>>>> "bodyMainContent" : false,
>>>>> "businessDataList" : [ ],
>>>>> "replyAuthorized" : false,
>>>>> "messageActions" : [ ]
>>>>> },
>>>>> "businessData" : [ ]
>>>>> }
>>>>> --uuid:eb20df03-1120-4e02-b5c5-cb2f050ea975
>>>>> Content-Type: application/pdf
>>>>> Content-Transfer-Encoding: binary
>>>>> Content-ID: <upfile1>
>>>>> Content-Disposition: form-data; name="upfile1";
>>>>> <...I skipped the pdf-data content...>
>>>>> --uuid:eb20df03-1120-4e02-b5c5-cb2f050ea975--
>>>>> -----Oorspronkelijk bericht-----
>>>>> Van: Jean Pierre URKENS <[email protected]>
>>>>> Verzonden: maandag 24 maart 2025 10:31
>>>>> Aan: 'Andriy Redko' <[email protected]>
>>>>> Onderwerp: RE: CXF JAX-RS: working with multipart form-data
>>>>> Hi andriy,
>>>>> The project was put on hold for some time. Now it kicks off and I had
>> a
>>>>> look at it again.
>>>>> Debugging the validation showed that method validation failed because
>>>> the
>>>>> required header parameters where null.
>>>>> The method signature includes two header parameters, so in my client
> I
>>>> had
>>>>> to add the headers:
>>>>> WebClient wc = WebClient.fromClient(client)
>>>>> .headers(headers)
>>>>> .path("/messages")
>>>>> .accept(MediaType.APPLICATION_JSON_TYPE);
>>>>> And initially I made the mistake to add the headers with a wrong
>> header
>>>>> name. I added them using the 'parameter name' in the method signature
>>>>> whereas I should have used the name indicated in the @HeaderParam
>>>>> annotation.
>>>>> Response createMessage(
>>>>> @HeaderParam("x-correlation-id") @NotNull
>>>>> @Size(min = 10, max = 36) @Parameter(description="ID of the
>>> transaction.
>>>>> Use this ID for log tracing and incident handling.") String
>>>>> xCorrelationId,
>>>>> @HeaderParam("Idempotency-Key") @NotNull
>>>> @Size(min
>>>>> = 10, max = 36) @Parameter(description="When retrying a failed call,
>>> the
>>>>> retry call should have the same Idempotency Key.") String
>>>> idempotencyKey,
>>>>> @FormDataParam(value="messageToSend")
>>>>> @Parameter(required=true,schema =
>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>> "messageToSend", type="application/json", required= true)
>> MessageToSend
>>>>> messageToSend,
>>>>> @FormDataParam(value="upfile1")
>>>> @Parameter(schema
>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>> "upfile1", type="application/pdf", required = false) InputStream
>>>>> upfile1Detail,
>>>>> @FormDataParam(value="upfile2")
>>>> @Parameter(schema
>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>> "upfile2", type="application/pdf", required = false) InputStream
>>>>> upfile2Detail,
>>>>> @FormDataParam(value="upfile3")
>>>> @Parameter(schema
>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>> "upfile3", type="application/pdf", required = false) InputStream
>>>>> upfile3Detail)
>>>>> throws GeneralSecurityException,
>>> AuthorizationException;
>>>>> I.e. I added a header with name 'xCorrelationId' whereas it should be
>> '
>>>>> x-correlation-id'.
>>>>> Fixing this also passed the validation (as otherwise the header
> params
>>>>> would be null and they are listed as required).
>>>>> So problem was at my side.
>>>>> Regards,
>>>>> J.P. Urkens
>>>>> -----Oorspronkelijk bericht-----
>>>>> Van: Andriy Redko <[email protected]>
>>>>> Verzonden: maandag 25 november 2024 3:33
>>>>> Aan: Jean Pierre URKENS <[email protected]>;
>>>>> [email protected]
>>>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>>>> Hi Jean,
>>>>> My apologies for the delay, took me a bit longer to find the time. So
>>>> I've
>>>>> added
>>>>> another test case to the existing pull request [1], that includes
>>>>> multipart and
>>>>> bean validation, no issues and the validation works as expected.
> Could
>>>> you
>>>>> help
>>>>> me out and point what I am doing differently (one difference is that
>>> the
>>>>> CXF tests
>>>>> use Hibernate Validator under the hood but it should not matter I
>>>> believe,
>>>>> in any
>>>>> case I will try to run the tests with the library you are using).
>>>>> Thanks.
>>>>> [1] https://github.com/apache/cxf/pull/2152
>>>>> Best Regards,
>>>>> Andriy Redko
>>>>>> If I deactivate the bean validation feature in
>>>> createMagdadocServer(...)
>>>>>> it works.
>>>>>> Some further questions:
>>>>>> 1. Setting the target address when using WebClient directly
>>>>>> ================================================
>>>>>> Currently I create my client using JAXRSClientFactoryBean
>> refereincing
>>>>> my
>>>>>> service class as follows (<PT> is either my service interface class
>> or
>>>>>> some interface extending on it):
>>>>>> protected synchronized PT getApiProxy() {
>>>>>> if (apiProxy == null) {
>>>>>> //Get the base address of the service
>> endpoint
>>>>>> String baseAddress =
> Configuration.getInstance().getItem("magdadocumentendienst.service.base.ur
>>>>>> i");
>>>>>> apiProxy = getThreadsafeProxy(baseAddress);
>>>>>> }
>>>>>> return apiProxy;
>>>>>> }
>>>>>> private PT getThreadsafeProxy(String baseAddress) {
>>>>>> JacksonJsonProvider jjProvider = new
>>>>>> JacksonJsonProvider(new CustomObjectMapper());
>>>>>> List<Object> providers = Arrays.asList(new
>>>>>> MultipartProvider(), jjProvider);
>>>>>> final JAXRSClientFactoryBean factory = new
>>>>>> JAXRSClientFactoryBean();
>>>>>> factory.setAddress(baseAddress);
>>>>>> factory.setServiceClass(getPTClass());
>>>>>> factory.setProviders(providers);
>>>>>> factory.setThreadSafe(true);
>>>>>> //only for testing the sending of attachments as
>>>>>> multipart/form-data
>>>>>> LoggingFeature feature = new LoggingFeature();
>>>>>> feature.setLogMultipart(true);
>>>>>> feature.setLogBinary(true);
>>>>>> feature.setLimit(128*1000);
>>> feature.addBinaryContentMediaTypes(MediaType.APPLICATION_OCTET_STREAM);
>> feature.addBinaryContentMediaTypes("application/pdf");
>>>>>> factory.setFeatures(Arrays.asList(feature));
>>>>>> PT api = (PT) factory.create(getPTClass());
>>>>>> ClientConfiguration config =
>> WebClient.getConfig(api);
>>>>>> addTLSClientParameters(config.getHttpConduit());
>>>>>> return api;
>>>>>> }
>>>>>> When invoking a method on it (e.g. getApiProxy().createMessage(...))
>>> it
>>>>> is
>>>>>> handled by cxf the class ClientProxyImpl. This uses the method
>>>> signature
>>>>>> and all annotated 'path' declarations to determine the target
>> address.
>>>>>> This doesn't happen when I invoke the call through a WebClient, e.g.
>>>> (as
>>>>>> to your suggestion):
>>>>>> MultipartBody body = new
>>>>>> MultipartBody(attachments,MediaType.MULTIPART_FORM_DATA_TYPE,false);
>>>>>> WebClient wc = WebClient.fromClient(client)
>>>>>> .path("/messages")
>>>>> .accept(MediaType.APPLICATION_JSON_TYPE);
>>>>>> return
>>>>>> wc.post(Entity.entity(body,MediaType.MULTIPART_FORM_DATA_TYPE));
>>>>>> Here I have to set the path manually. Is there a way to construct it
>>>>>> similar to what ClientProxyImpl does?
>>>>>> 2.Bean validation
>>>>>> ==============
>>>>>> I guess there are different bean validation frameworks/libraries.
> I'd
>>>>> like
>>>>>> to keep bean validation active any suggestions on how to
>>>>>> resolve/circumvent this issue, e.g.:
>>>>>> - using another bean validation library
>>>>>> - disabling validation for a specific method (is this possible?)
>>>>>> - ....
>>>>>> Regards,
>>>>>> J.P. Urkens
>>>>>> -----Oorspronkelijk bericht-----
>>>>>> Van: Jean Pierre URKENS <[email protected]>
>>>>>> Verzonden: maandag 18 november 2024 10:20
>>>>>> Aan: 'Andriy Redko' <[email protected]>; '[email protected]'
>>>>>> <[email protected]>
>>>>>> Onderwerp: RE: CXF JAX-RS: working with multipart form-data
>>>>>> Validation is done using apache library bval-jsr-2.0.5.jar. So this
>>>>> might
>>>>>> be well out-of-scope of CXF.
>>>>>> -----Oorspronkelijk bericht-----
>>>>>> Van: Jean Pierre URKENS <[email protected]>
>>>>>> Verzonden: maandag 18 november 2024 10:02
>>>>>> Aan: 'Andriy Redko' <[email protected]>; '[email protected]'
>>>>>> <[email protected]>
>>>>>> Onderwerp: RE: CXF JAX-RS: working with multipart form-data
>>>>>> Hi Andriy,
>>>>>> Thanks for the example. I tried something similar last week but
> since
>>>> my
>>>>>> method was annotated with @Parameter(required=true,schema =
>>>>>> @Schema(implementation=MessageToSend.class)) request parameter
>>>>> validation
>>>>>> failed on the server side (see server trace below).
>>>>>> I have a JAXRSBeanValidationInInterceptor provider active at the
>>> server
>>>>>> side and somehow matching the multipart request body to the method
>>>>>> signature fails.
>>>>>> So does it mean that validating request parameters takes place
> before
>>>>>> handling the multipart body translating each part to a method
>>>> parameter?
>>>>>> My actual method signature looked like:
>>>>>> Response createMessage(
>>>>>> @HeaderParam("x-correlation-id") @NotNull
>>>>>> @Size(min = 10, max = 36) @Parameter(description="ID of the
>>>> transaction.
>>>>>> Use this ID for log tracing and incident handling.") String
>>>>>> xCorrelationId,
>>>>>> @HeaderParam("Idempotency-Key") @NotNull
>>>>> @Size(min
>>>>>> = 10, max = 36) @Parameter(description="When retrying a failed call,
>>>> the
>>>>>> retry call should have the same Idempotency Key.") String
>>>>> idempotencyKey,
>>>>>> @FormDataParam(value="messageToSend")
>>>>>> @Parameter(required=true,schema =
>>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>>> "messageToSend", type="application/json", required= true)
>>> MessageToSend
>>>>>> messageToSend,
>>>>>> @FormDataParam(value="upfile1")
>>>>> @Parameter(schema
>>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>> "upfile1", type="application/pdf", required = false) InputStream
>>>>>> upfile1Detail,
>>>>>> @FormDataParam(value="upfile2")
>>>>> @Parameter(schema
>>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>> "upfile2", type="application/pdf", required = false) InputStream
>>>>>> upfile2Detail,
>>>>>> @FormDataParam(value="upfile3")
>>>>> @Parameter(schema
>>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>> "upfile3", type="application/pdf", required = false) InputStream
>>>>>> upfile3Detail)
>>>>>> throws GeneralSecurityException, AuthorizationException;
>>>>>> Even if I reduce the method signature to:
>>>>>> Response createMessage(
>>>>>> @HeaderParam("x-correlation-id") @NotNull
>>>>>> @Size(min = 10, max = 36) @Parameter(description="ID of the
>>>> transaction.
>>>>>> Use this ID for log tracing and incident handling.") String
>>>>>> xCorrelationId,
>>>>>> @HeaderParam("Idempotency-Key") @NotNull
>>>>> @Size(min
>>>>>> = 10, max = 36) @Parameter(description="When retrying a failed call,
>>>> the
>>>>>> retry call should have the same Idempotency Key.") String
>>>>> idempotencyKey,
>>>>>> @Multipart(value = "messageToSend",
>>>>>> type="application/json", required= true) MessageToSend
> messageToSend,
>>>>>> @Multipart(value = "upfile1",
>>>>>> type="application/pdf", required = false) InputStream upfile1Detail,
>>>>>> @Multipart(value = "upfile2",
>>>>>> type="application/pdf", required = false) InputStream upfile2Detail,
>>>>>> @Multipart(value = "upfile3",
>>>>>> type="application/pdf", required = false) InputStream upfile3Detail)
>>>>>> throws GeneralSecurityException, AuthorizationException;
>>>>>> I am still getting a '500 server error' saying that arg0 of
>>>>> createMessage
>>>>>> may not be null.
>>>>>> Here are some extracts from the code:
>>>>>> 1.API interface (only createMessage shown)
>>>>>> ====================================
>>>>>> @POST
>>>>>> @Path("/messages")
>>>>>> @Consumes("multipart/form-data")
>>>>>> @Produces({ "application/json" })
>>>>>> @Operation(
>>>>>> summary = "Send a message, using a channel
>>>>> (email,
>>>>>> paper mail, ebox) and delivery method (registered or normal) of your
>>>>>> choice. More than 6 upfiles only supported for PAPER delivery.",
>>>>>> tags = {"messages" },
>>>>>> operationId="createMessage",
>>>>>> security=@SecurityRequirement(name="BearerAuthentication"))
>>>>>> @ApiResponses({
>>>>>> @ApiResponse(
>>>>>> responseCode = "201",
>>>>>> description = "Created",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,array=@ArraySchema(schema=@S
>>>>>> chema(implementation=SendStatusMessage.class))),
>>>>>> headers = {@Header(
>>>>>> name="X-Magda-Exceptions",
>>>>>> required=false,
>>>>>> description="Only used in the context of EBOX delivery and if there
>>> was
>>>>> a
>>>>>> problem with the consent of the receiver's ebox.",
>>>>>> schema=@Schema(implementation=MagdaExceptionList.class))
>>>>>> }),
>>>>>> @ApiResponse(
>>>>>> responseCode = "400",
>>>>>> description = "Invalid data
>>>>>> supplied",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
>>>>>> n=ErrorMessage.class))),
>>>>>> @ApiResponse(
>>>>>> responseCode = "401",
>>>>>> description = "Invalid
>>>>>> authorization",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
>>>>>> n=ErrorMessage.class))),
>>>>>> @ApiResponse(
>>>>>> responseCode = "500",
>>>>>> description = "Unexpected
>>>> Server
>>>>>> Error",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
>>>>>> n=ErrorMessage.class))),
>>>>>> @ApiResponse(
>>>>>> responseCode = "502",
>>>>>> description = "Bad Gateway",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
>>>>>> n=ErrorMessage.class))),
>>>>>> @ApiResponse(
>>>>>> responseCode = "503",
>>>>>> description = "Service
>>>>>> unavailable",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
>>>>>> n=ErrorMessage.class))),
>>>>>> @ApiResponse(
>>>>>> responseCode = "504",
>>>>>> description = "Gateway
>>>> Timeout",
>>>>>> content =
> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implementatio
>>>>>> n=ErrorMessage.class)))
>>>>>> })
>>>>>> Response createMessage(
>>>>>> @HeaderParam("x-correlation-id") @NotNull
>>>>>> @Size(min = 10, max = 36) @Parameter(description="ID of the
>>>> transaction.
>>>>>> Use this ID for log tracing and incident handling.") String
>>>>>> xCorrelationId,
>>>>>> @HeaderParam("Idempotency-Key") @NotNull
>>>>> @Size(min
>>>>>> = 10, max = 36) @Parameter(description="When retrying a failed call,
>>>> the
>>>>>> retry call should have the same Idempotency Key.") String
>>>>> idempotencyKey,
>>>>>> @FormDataParam(value="messageToSend")
>>>>>> @Parameter(required=true,schema =
>>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>>> "messageToSend", type="application/json", required= true)
>>> MessageToSend
>>>>>> messageToSend,
>>>>>> @FormDataParam(value="upfile1")
>>>>> @Parameter(schema
>>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>> "upfile1", type="application/pdf", required = false) InputStream
>>>>>> upfile1Detail,
>>>>>> @FormDataParam(value="upfile2")
>>>>> @Parameter(schema
>>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>> "upfile2", type="application/pdf", required = false) InputStream
>>>>>> upfile2Detail,
>>>>>> @FormDataParam(value="upfile3")
>>>>> @Parameter(schema
>>>>>> = @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>> "upfile3", type="application/pdf", required = false) InputStream
>>>>>> upfile3Detail)
>>>>>> throws GeneralSecurityException, AuthorizationException;
>>>>>> 2. Client Invocation (only createMessage)
>>>>>> =================================
>>>>>> public Response createMessage(String xCorrelationId,String
>>>>>> idempotencyKey,MessageToSend messageToSend,
>>>>>> InputStream upfile1,
>>>>>> InputStream upfile2,
>>>>>> InputStream upfile3)
>>>>>> throws GeneralSecurityException, AuthorizationException {
>>>>>> //Create a multipartbody
>>>>>> List<Attachment> attachments = new ArrayList<>();
>>>>>> attachments.add(new
> AttachmentBuilder().id("messageToSend").object(messageToSend).contentDispo
>>>>>> sition(new ContentDisposition("form-data;
>>>>>> name=\"messageToSend\";")).mediaType("application/json").build());
>>>>>> if (upfile1 != null) {
>>>>>> attachments.add(new AttachmentBuilder()
>>>>>> .id("upfile1")
>>>>>> .dataHandler(new
>>>> DataHandler(new
>>>>>> InputStreamDataSource(upfile1,"application/pdf","upfile1")))
>>>>>> .contentDisposition(new
>>>>>> ContentDisposition("form-data; name=\"upfile1\";"))
>>>>>> .build());
>>>>>> }
>>>>>> if (upfile2 != null) {
>>>>>> attachments.add(new AttachmentBuilder()
>>>>>> .id("upfile2")
>>>>>> .dataHandler(new
>>>> DataHandler(new
>>>>>> InputStreamDataSource(upfile2,"application/pdf","upfile2")))
>>>>>> .contentDisposition(new
>>>>>> ContentDisposition("form-data; name=\"upfile1\";"))
>>>>>> .build());
>>>>>> }
>>>>>> if (upfile3 != null) {
>>>>>> attachments.add(new AttachmentBuilder()
>>>>>> .id("upfile3")
>>>>>> .dataHandler(new
>>>> DataHandler(new
>>>>>> InputStreamDataSource(upfile3,"application/pdf","upfile3")))
>>>>>> .contentDisposition(new
>>>>>> ContentDisposition("form-data; name=\"upfile1\";"))
>>>>>> .build());
>>>>>> }
>>>>>> MultipartBody body = new
>>>>>> MultipartBody(attachments,MediaType.MULTIPART_FORM_DATA_TYPE,false);
>>>>>> //Authorize API client
>>>>>> Client client = WebClient.client(getApiProxy());
> authorizationHandler.authorize(client,resourceClientId,consumerClientId,nu
>>>>>> ll,consumerPrivKey);
>>>>>> WebClient wc = WebClient.fromClient(client)
>>>>>> .path("/messages") /*UJ: Is there a
>>> way
>>>>> to
>>>>>> construct the path as is done in ClientProxyImpl making use of the
>>> path
>>>>>> annotations on the method. */
>>>>> .accept(MediaType.APPLICATION_JSON_TYPE);
>>>>>> return
>>>>>> wc.post(Entity.entity(body,MediaType.MULTIPART_FORM_DATA_TYPE));
>>>>>> }
>>>>>> 3. Server Implementation
>>>>>> =====================
>>>>>> /**
>>>>>> * {@inheritDoc}
>>>>>> * @see
> be.dvtm.aeo.common.magda.documenten.api.MessagesApi#createMessage(java.lan
>>>>>> g.String, java.lang.String,
>>>>>> be.dvtm.aeo.common.magda.documenten.model.MessageToSend,
>>>>>> java.io.InputStream, java.io.InputStream, java.io.InputStream)
>>>>>> */
>>>>>> @Override
>>>>>> public Response createMessage(
>>>>>> String xCorrelationId,
>>>>>> String idempotencyKey,
>>>>>> MessageToSend messageToSend,
>>>>>> InputStream upfile1,
>>>>>> InputStream upfile2,
>>>>>> InputStream upfile3) throws
>>>>>> GeneralSecurityException, AuthorizationException {
>>>>>> //NOT REALLY RELEVANT AS IT DOESN'T GET THIS FAR due to
>>> failing
>>>>>> validation
>>>>>> }
>>>>>> 4. Testcase
>>>>>> =========
>>>>>> //The server is setup as follows:
>>>>>> private Server createMagdadocServer(String baseAddress) {
>>>>>> //Create a Server instance
>>>>>> final JAXRSServerFactoryBean factory = new
>>>>>> JAXRSServerFactoryBean();
>>>>>> factory.setAddress(baseAddress);
>>>>>> //01.Set root resource class and provider
>>> factory.setResourceClasses(MagdadocSimulatorApi.class);
>>> factory.setResourceProvider(MagdadocSimulatorApi.class,
>>>>>> new SingletonResourceProvider(new MagdadocSimulator(), true));
>>>>>> //02.set Logging feature
>>>>>> List<Feature> featureList = new
> ArrayList<Feature>();
>>>>>> featureList.add(new LoggingFeature());
>>>>>> factory.setFeatures(featureList);
>>>>>> //03.activate wadl generator (just too see what the
>>>>> server
>>>>>> has deployed)
>>>>>> WadlGenerator wadlGen = new WadlGenerator();
>>>>>> wadlGen.setLinkAnyMediaTypeToXmlSchema(true);
>>>>>> //04.Set Providers
>>>>>> List<Object> providers = new ArrayList<Object>();
>>>>>> providers.add(new JacksonJsonProvider(new
>>>>>> CustomObjectMapper()));
>>>>>> providers.add(new MultipartProvider());
>>>>>> providers.add(wadlGen);
>>>>>> factory.setProviders(providers);
>>>>>> //05. Set interceptors
>>>>>> JAXRSBeanValidationInInterceptor
>>>> validationInInterceptor
>>>>> =
>>>>>> new JAXRSBeanValidationInInterceptor();
>>>>>> validationInInterceptor.setProvider(new
>>>>>> BeanValidationProvider());
>>>>>> List<Interceptor<? extends Message>> interceptors =
>>> new
>>>>>> ArrayList<>();
>>>>>> interceptors.add(validationInInterceptor);
>>>>>> factory.setInInterceptors(interceptors);
>>>>>> return factory.create();
>>>>>> }
>>>>>> 5.Server LOG
>>>>>> ===========
>>>>>> [MAGDADOC] 2024-11-18 09:16:10,089 [qtp681015501-59] INFO $--$
>>>>>> (org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) - REQ_IN
>>>>>> Address: http://localhost:8091/services/magdadoc/messages
>>>>>> HttpMethod: POST
>>>>>> Content-Type: multipart/form-data;
>>>>>> boundary="uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190"
>>>>>> ExchangeId: 1eabcefe-eb7b-4d59-af35-2c3e72c8674a
>>>>>> Headers: {transfer-encoding=chunked, Accept=application/json,
>>>>>> Cache-Control=no-cache, User-Agent=Apache-CXF/3.5.8,
>>>>>> connection=keep-alive, content-type=multipart/form-data;
>>>>>> boundary="uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190",
>>>>> Host=localhost:8091,
>>>>>> Pragma=no-cache}
>>>>>> Payload:
>>>>>> --uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190
>>>>>> Content-Type: application/json
>>>>>> Content-Transfer-Encoding: binary
>>>>>> Content-ID: <messageToSend>
>>>>>> Content-Disposition: form-data; name="messageToSend";
>>>>>> {
>>>>>> "delivery" : "EBOX",
>>>>>> "eboxDeliveryData" : {
>>>>>> "recipient" : {
>>>>>> "eboxType" : "ENTERPRISE",
>>>>>> "ssin" : null,
>>>>>> "enterpriseNumber" : "0123456789",
>>>>>> "partition" : null,
>>>>>> "eboxIdValue" : "0123456789"
>>>>>> },
>>>>>> "forTheAttentionOf" : null,
>>>>>> "originalMessageId" : null,
>>>>>> "subject" : {
>>>>>> "nl" : "ProjectOnontvankelijkMail Project: 2025-EP-0001",
>>>>>> "fr" : null,
>>>>>> "de" : null
>>>>>> },
>>>>>> "messageTypeId" : null,
>>>>>> "expirationDate" : null,
>>>>>> "senderOrganizationId" : null,
>>>>>> "senderApplicationId" : null,
>>>>>> "registeredMail" : false,
>>>>>> "attachments" : [ {
>>>>>> "attachmentTitle" : null,
>>>>>> "httpPartName" : "upfile1",
>>>>>> "mainContent" : true,
>>>>>> "digest" : null,
>>>>>> "attachmentSigned" : false
>>>>>> }, {
>>>>>> "attachmentTitle" : null,
>>>>>> "httpPartName" : "upfile2",
>>>>>> "mainContent" : false,
>>>>>> "digest" : null,
>>>>>> "attachmentSigned" : false
>>>>>> }, {
>>>>>> "attachmentTitle" : null,
>>>>>> "httpPartName" : "upfile3",
>>>>>> "mainContent" : false,
>>>>>> "digest" : null,
>>>>>> "attachmentSigned" : false
>>>>>> } ],
>>>>>> "bodyMainContent" : false,
>>>>>> "bodyContent" : null,
>>>>>> "businessDataList" : [ ],
>>>>>> "messageData" : null,
>>>>>> "paymentData" : null,
>>>>>> "replyAuthorized" : false,
>>>>>> "replyDueDate" : null,
>>>>>> "messageActions" : [ ],
>>>>>> "erroneousMessageId" : null
>>>>>> },
>>>>>> "paperDeliveryData" : null,
>>>>>> "emailDeliveryData" : null,
>>>>>> "businessData" : [ ]
>>>>>> }
>>>>>> --uuid:2dc8ff1d-c69a-41c4-93ca-0f3b2bc2b190
>>>>>> --- Content suppressed ---
>>>>>> --- Content suppressed ---
>>>>>> --- Content suppressed ---
>>>>>> [MAGDADOC] 2024-11-18 09:16:10,124 [qtp681015501-59] INFO $--$
>>>>>> (org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) - FAULT_OUT
>>>>>> Content-Type: application/json
>>>>>> ResponseCode: 500
>>>>>> ExchangeId: 1eabcefe-eb7b-4d59-af35-2c3e72c8674a
>>>>>> Headers: {}
>>>>>> Payload: <ns1:XMLFault
>>>>>> xmlns:ns1="http://cxf.apache.org/bindings/xformat"><ns1:faultstring
> xmlns:ns1="http://cxf.apache.org/bindings/xformat">javax.validation.Constr
>>>>>> aintViolationException: createMessage.arg0: may not be null,
>>>>>> createMessage.arg1: may not be null</ns1:faultstring></ns1:XMLFault>
>>>>>> -----Oorspronkelijk bericht-----
>>>>>> Van: Andriy Redko <[email protected]>
>>>>>> Verzonden: maandag 18 november 2024 0:07
>>>>>> Aan: Jean Pierre URKENS <[email protected]>;
>>>>>> [email protected]
>>>>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>>>>> Hi Jean,
>>>>>> So I have been able to spend some time on the issue and it seems
> like
>>>>> you
>>>>>> might not be using the client properly (hence getting the
>> exceptions),
>>>>>> just a hypothesis.
>>>>>> Here I have crafted a version of the API:
>>>>>> @POST
>>>>>> @Path("/multipart")
>>>>>> @Consumes("multipart/form-data")
>>>>>> @Produces("text/xml")
>>>>>> public Response addParts(@Multipart(value = "messageToSend",
>>>>>> type="application/xml") MessageToSend messageToSend,
>>>>>> @Multipart("upfile1Detail") Attachment a1,
>>>>>> @Multipart("upfile2Detail") Attachment a2,
>>>>>> @Multipart("upfile3Detail") Attachment
> a3)
>>>>>> ...
>>>>>> }
>>>>>> And the client invocation sequence:
>>>>>> final Client client = ClientBuilder.newClient();
>>>>>> final MultipartBody builder = new
>> MultipartBody(Arrays.asList(
>>>>>> new AttachmentBuilder()
>>>>>> .mediaType("application/xml")
>>>>>> .id("messageToSend")
>>>>>> .object(new MessageToSend())
>>>>>> .build(),
>>>>>> new AttachmentBuilder()
>>>>>> .id("upfile1Detail")
>>>>>> .dataHandler(new DataHandler(new
> InputStreamDataSource(getClass().getResourceAsStream("/org/apache/cxf/syst
>>>>>> est/jaxrs/resources/attachmentData"), "text/xml")))
>>>>>> .contentDisposition(new
>> ContentDisposition("form-data;
>>>>>> name=\"field1\";"))
>>>>>> .build(),
>>>>>> new AttachmentBuilder()
>>>>>> .id("upfile2Detail")
>>>>>> .dataHandler(new DataHandler(new
>>>>> InputStreamDataSource(new
>>>>>> ByteArrayInputStream(new byte[0]), "text/xml")))
>>>>>> .contentDisposition(new
>> ContentDisposition("form-data;
>>>>>> name=\"field2\";"))
>>>>>> .build(),
>>>>>> new AttachmentBuilder()
>>>>>> .id("upfile3Detail")
>>>>>> .dataHandler(new DataHandler(new
>>>>> InputStreamDataSource(new
>>>>>> ByteArrayInputStream(new byte[0]), "text/xml")))
>>>>>> .contentDisposition(new
>> ContentDisposition("form-data;
>>>>>> name=\"field3\";"))
>>>>>> .build()));
>>>>>> final Response response = client
>>>>>> .target(address)
>>>>>> .request("text/xml")
>>>>>> .post(Entity.entity(builder,
> "multipart/form-data"));
>>>>>> It works perfectly when the unified Attachment body part is used. I
>>>> also
>>>>>> crafted the test case over
>>>>>> here [1], to help you out to get it working or point me out if there
>>> is
>>>>> a
>>>>>> gap here that I missed.
>>>>>> Thank you.
>>>>>> [1] https://github.com/apache/cxf/pull/2152
>>>>>> Best Regards,
>>>>>> Andriy Redko
>>>>>>> Hi Andriy,
>>>>>>> The option to use a List<Attachment> or a MultipartBody does work,
>>>> I've
>>>>>>> testcases to confirm this.
>>>>>>> But it somehow breaks the original spec since trying to do a round
>>>> trip
>>>>>>>>> spec),
>>>>>>> the spec generated from the code (based on annotations) no longer
>>>>>>> reflects the input spec.
>>>>>>> What I find unexpected is that for multipart bodies all input
>>>>> parameters
>>>>>>> are attempted to be wrapped into Attachment objects,
>>>>>>> (cf. method
> org.apache.cxf.jaxrs.client.ClientProxyImpl#handleMultipart(MultivaluedMap
>>>>>>> <ParameterType, Parameter> map,OperationResourceInfo ori,Object[]
>>>>>>> params)).
>>>>>>> So why doesn't the stack allow to mix request body parameters that
>>> are
>>>>>>> either @Multipart annotated or are Attachment itself.
>>>>>>> Now you can't mix them since
> org.apache.cxf.jaxrs.client.ClientProxyImpl#getParametersInfo(Method
>>>>>>> m,Object[] params, OperationResourceInfo ori) will fail
>>>>>>> with error "SINGLE_BODY_ONLY". It wouldn't be hard to support the
>>>>>> mixture
>>>>>>> of both, or even an @Multipart annotated Attachment parameter (you
>>>>>> could
>>>>>>> just combine what is specified in the annotation with what is
>> already
>>>>>>> present in the Attachment, giving priority to one of both in case
> of
>>>>>>> overlapping parameters).
>>>>>>> Further, if 'Content-Disposition' is obligatory (at least by
> openAPI
>>>>>> spec,
>>>>>>> however don't know whether this is the industry reference) why
>>> doesn't
>>>>>> the
>>>>>>> @Multipart
>>>>>>> annotation allow to specify it? Why i.o. setting header
>>>>>> Content-ID=<value>
>>>>>>> isn't the header Content-Disposition=form-date:name="value" set
> when
>>>>>>> wrapping
>>>>>>> a @Multipart annotated object into an Attachment object?
>>>>>>> Strangely I don't even find a reference to the header Content-ID in
>>>>>>> https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers. It is
>>>>>> described
>>>>>>> in
>>>>>>> https://www.rfc-editor.org/rfc/rfc2045#section-7,
>>>>>>> https://www.rfc-editor.org/rfc/rfc2392.txt and should have the form
>>>>>>> 'url-addr-spec according to RFC822'
>>>>>>> enclosed within '<>' and it is used to reference a multipartbody
>> part
>>>>> in
>>>>>>> another part of the message. This doesn't seem to be the context in
>>>>>> which
>>>>>>> it is used in
>>>>>>> JAX-RS messages, further the url-addr-spec actually tells me there
>>>>>> should
>>>>>>> a ' @' sign in the value of the content-id header which is surely
>> not
>>>>>> the
>>>>>>> case in all examples
>>>>>>> I've seen sofar. So why is CXF even using Content-ID?
>>>>>>> Regards,
>>>>>>> J.P.
>>>>>>> -----Oorspronkelijk bericht-----
>>>>>>> Van: Andriy Redko <[email protected]>
>>>>>>> Verzonden: vrijdag 15 november 2024 21:02
>>>>>>> Aan: Jean Pierre URKENS <[email protected]>;
>>>>>>> [email protected]
>>>>>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>>>>>> Hi Jean,
>>>>>>> Sorry for the delay, just went over through the message thread. So
>>>>>> option
>>>>>>> 1-2 should
>>>>>>> indeed work just fine. And for 1st option, you could indeed set the
>>>>>>> headers manually.
>>>>>>> And in this case, you will need to craft the OpenAPI spec manually,
>>> it
>>>>>>> won't be properly
>>>>>>> deducted.
>>>>>>> I don't think adding more attributes to @Multipart would help since
>>> it
>>>>>> is
>>>>>>> going to
>>>>>>> in conflict with File / Attachment that by itself source these
>>>>>> attributes.
>>>>>>> But gimme
>>>>>>> some time to experiment over the weekend, I think, intuitively,
> that
>>>>>> this
>>>>>>> could work
>>>>>>> (doesn't right now):
>>>>>>> Response testMessage1(
>>>>>>> @HeaderParam("x-correlation-id") @NotNull
>>>>>>> @Size(min = 10, max = 36)
>>>>>>> @Parameter(description="ID of the transaction. Use this ID for log
>>>>>> tracing
>>>>>>> and incident handling.") String xCorrelationId,
>>>>>>> @HeaderParam("Idempotency-Key") @NotNull
>>>>>> @Size(min
>>>>>>> = 10, max = 36)
>>>>>>> @Parameter(description="When retrying a failed call, the retry call
>>>>>> should
>>>>>>> have the same Idempotency Key.") String idempotencyKey,
>>>>>>> @FormDataParam(value="messageToSend")
>>>>>>> @Parameter(required=true,schema =
>>>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>>>> "messageToSend", type="application/json", required= true)
>>>> MessageToSend
>>>>>>> messageToSend,
>>>>>>> @FormDataParam(value="upfile1")
>>>>>> @Parameter(schema
>>>>>>> = @Schema(type =
>>>>>>> "string", format = "binary")) Attachment upfile1Detail,
>>>>>>> @FormDataParam(value="upfile2")
>>>>>> @Parameter(schema
>>>>>>> = @Schema(type =
>>>>>>> "string", format = "binary")) Attachment upfile2Detail,
>>>>>>> @FormDataParam(value="upfile3")
>>>>>> @Parameter(schema
>>>>>>> = @Schema(type =
>>>>>>> "string", format = "binary")) Attachment upfile3Detail)
>>>>>>> If we could fold it into :
>>>>>>> Response testMessage1(
>>>>>>> @HeaderParam("x-correlation-id") @NotNull
>>>>>>> @Size(min = 10, max = 36)
>>>>>>> @Parameter(description="ID of the transaction. Use this ID for log
>>>>>> tracing
>>>>>>> and incident handling.") String xCorrelationId,
>>>>>>> @HeaderParam("Idempotency-Key") @NotNull
>>>>>> @Size(min
>>>>>>> = 10, max = 36)
>>>>>>> @Parameter(description="When retrying a failed call, the retry call
>>>>>> should
>>>>>>> have the same Idempotency Key.") String idempotencyKey,
>>>>>>> @FormDataParam(value="messageToSend")
>>>>>>> @Parameter(required=true,schema =
>>>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>>>> "messageToSend", type="application/json", required= true)
>>>> MessageToSend
>>>>>>> messageToSend,
>>>>>>> List<Attachment> attachments)
>>>>>>> This is just rough idea, but I will try look more closely into it.
>>>>>>> Thanks.
>>>>>>> Best Regards,
>>>>>>> Andriy Redko
>>>>>>>> What I found out when trying to send multipart/form-data requests
>>>> with
>>>>>>> CXF
>>>>>>>> v3.5:
>>>>>>>> 1. You can have just one request body parameter. This can be a
>>>>>>>> MultipartBody, or a List<Attachment> but you'll have to set the
>>>>> headers
>>>>>>>> (Content-ID,Content-Type, Content-Disposition) yourself.
>>>>>>>> 2. I believe you can also have just one request body parameter
>>>>>>>> representing a List<Files> or a single File. Here a
>>>>> Content-Disposition
>>>>>>>> header will be set based on the filename (didn't check this, but
>> the
>>>>>>> code
>>>>>>>> seems to reveal this).
>>>>>>>> 3. You can have just one request parameter annotated with
>>> @Multipart.
>>>>>>> This
>>>>>>>> will add the Content-ID and Content-Type headers based on the
>>>>>>> annotation,
>>>>>>>> but not the Content-Disposition.
>>>>>>>> 4. You can have multiple request body parameters but then all of
>>> them
>>>>>>> need
>>>>>>>> to be annotated with @Multipart. This will add the headers
>>>>> Content-Type
>>>>>>>> and Content-ID as specified in the annotation, but it will not add
>> a
>>>>>>>> 'Content-Disposition' header.
>>>>>>>> So the OpenAPI specification/examples as enlisted below requires
> us
>>>> to
>>>>>>>> convert everything to attachments (to get the Content-Disposition
>>>>>> header
>>>>>>>> in place) and either send a request body consisting of a
>>>>>>> List<Attachment>
>>>>>>>> or MultipartBody.
>>>>>>>> The fact that in CXF-v3.5.x you can not:
>>>>>>>> * specify a Content-Disposition header for a @Multipart input
>>>>>> parameter
>>>>>>>> * mix Attachment objects with @Multipart annotated objects (which
>>> by
>>>>>>> the
>>>>>>>> stack are converted to Attachment objects) as a list of input
>>>>>> parameters
>>>>>>>> seems to be a short-coming. It doesn't align well with the
> OpenAPI
>>>>>>> v3.0.x
>>>>>>>> specification.
>>>>>>>> Is this a correct conclusion?
>>>>>>>> Regards,
>>>>>>>> J.P. Urkens
>>>>>>>> -----Oorspronkelijk bericht-----
>>>>>>>> Van: Jean Pierre URKENS <[email protected]>
>>>>>>>> Verzonden: donderdag 14 november 2024 11:29
>>>>>>>> Aan: 'Andriy Redko' <[email protected]>; '[email protected]'
>>>>>>>> <[email protected]>
>>>>>>>> Onderwerp: RE: CXF JAX-RS: working with multipart form-data
>>>>>>>> Hi Andriy,
>>>>>>>> Actually, I think you'll have to set the Content-Disposition
>>> yourself
>>>>>> in
>>>>>>>> the Attachment object, while for File objects it will be retrieved
>>>>> from
>>>>>>>> the file name.
>>>>>>>> Looking at
> https://swagger.io/docs/specification/v3_0/describing-request-body/multipa
>>>>>>>> rt-requests/ how would you translate the request body to an method
>>>>>>>> signature that works with CXF (v3.5.x)?
>>>>>>>> The example shows the 'Content-Disposition' header to be present
>> for
>>>>>> all
>>>>>>>> multipart parts irrespective of their data type and that is
>>> something
>>>>> I
>>>>>>>> don't know how to achieve in a clean way using CXF. The
>>>>>>>> @org.apache.cxf.jaxrs.ext.multipart.Multipart annotation won't do
>>> the
>>>>>>> job
>>>>>>>> and CXF only adds it for File objects and for Attachment objects
>> (if
>>>>> it
>>>>>>> is
>>>>>>>> present in the Attachment object).
>>>>>>>> Regards,
>>>>>>>> J.P.
>>>>>>>> -----Oorspronkelijk bericht-----
>>>>>>>> Van: Andriy Redko <[email protected]>
>>>>>>>> Verzonden: woensdag 13 november 2024 23:42
>>>>>>>> Aan: Jean Pierre URKENS <[email protected]>;
>>>>>>>> [email protected]
>>>>>>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>>>>>>> Hi Jean,
>>>>>>>>> When looking at the classes MultipartProvider and JAXRSUtils (cxf
>>>>>>>> v3.5.9)
>>>>>>>>> then it shows that only object for which a 'Content-Disposition'
>>>>>>>> header will
>>>>>>>>> be written is a File Object. The problem is that my application
> is
>>>>>>>>> generating the file content on the fly, so I have it either as a
>>>>>>>> byte[] or
>>>>>>>>> InputStream.
>>>>>>>> I believe the 'Content-Disposition' will be written for File and
>>>>>>>> Attachment. Respectively,
>>>>>>>> it is going to be read for these multipart content parts as well.
>>>> This
>>>>>>> is
>>>>>>>> why the
>>>>>>>> @Multipart annotation has no 'Content-Disposition' or alike (I
>>>> think).
>>>>>>>>>> Even passing a List<Attachment> doesn't work as the
>>>>>>>> MultiPartProvider will
>>>>>>>>> loop through the list and try to create a DataHandler for an
>>>>>>>> Attachment
>>>>>>>>> object which is also not supported (throws an exception).
>>>>>>>> This is surprising, I will take a look shortly why it does not
>> work.
>>>>>>> What
>>>>>>>> kind of
>>>>>>>> exception are you getting?
>>>>>>>> Thank you.
>>>>>>>> Best Regards,
>>>>>>>> Andriy Redko
>>>>>>>>> Hi Andriy,
>>>>>>>>> When looking at the classes MultipartProvider and JAXRSUtils (cxf
>>>>>>>> v3.5.9)
>>>>>>>>> then it shows that only object for which a 'Content-Disposition'
>>>>>>>> header will
>>>>>>>>> be written is a File Object. The problem is that my application
> is
>>>>>>>>> generating the file content on the fly, so I have it either as a
>>>>>>>> byte[] or
>>>>>>>>> InputStream.
>>>>>>>>> This surprises me as according to
> https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposit
>>>>>>>> ion:
>>>>>>>>> "a multipart/form-data body requires a Content-Disposition header
>>> to
>>>>>>>> provide
>>>>>>>>> information about each subpart of the form (e.g., for every form
>>>>>>>> field and
>>>>>>>>> any files that are part of field data)".
>>>>>>>>> Also the Multipart annotation class only allows to specify
>>>>>>>> Content-Type,
>>>>>>>>> Content-ID so there is no way for me to provide
>>>> 'Content-Disposition'
>>>>>>>>> information on objects like byte[] or InputStream.
>>>>>>>>> Even passing a List<Attachment> doesn't work as the
>>>> MultiPartProvider
>>>>>>>> will
>>>>>>>>> loop through the list and try to create a DataHandler for an
>>>>>>>> Attachment
>>>>>>>>> object which is also not supported (throws an exception).
>>>>>>>>> The only way I see to pass it is to construct Attachment objects
>>> for
>>>>>>>> each
>>>>>>>>> multipart part, with 'Content-Disposition' set, and add them all
>> to
>>>> a
>>>>>>>>> MultipartBody object and pass this as input parameter to my
> method
>>>>>>>>> signature. But then I loose all swager information for input
>>> objects
>>>>>>>> that
>>>>>>>>> are not byte[] or InputStream.
>>>>>>>>> Am I missing something?
>>>>>>>>> Regards,
>>>>>>>>> J.P.
>>>>>>>>> -----Oorspronkelijk bericht-----
>>>>>>>>> Van: Andriy Redko <[email protected]>
>>>>>>>>> Verzonden: vrijdag 4 oktober 2024 2:52
>>>>>>>>> Aan: Jean Pierre URKENS <[email protected]>;
>>>>>>>>> [email protected]
>>>>>>>>> Onderwerp: Re: CXF JAX-RS: working with multipart form-data
>>>>>>>>> Hi Jean,
>>>>>>>>> Yeah, I think the @Multipart + Attachment may not work, but you
>>>> could
>>>>>>>> accept
>>>>>>>>> the List<Attachment> instead, right? (since you send many).
>>>>>>>>> The logging configuration does not seem right: you use
>> interceptors
>>>>>>>> AND
>>>>>>>>> feature (as per snippet below).
>>>>>>>>> factory.getOutInterceptors().add(new
>>>>>>>>> LoggingOutInterceptor());
>>>>>>>>> factory.getInInterceptors().add(new
>>>>>>>>> LoggingInInterceptor());
>>>>>>>>> LoggingFeature feature = new LoggingFeature();
>>>>>>>>> feature.setLogMultipart(true);
>>>>>>>>> feature.setLogBinary(true);
>>>>>>>>> ...
>>>>>>>>> You only need one of those, either interceptors (please configure
>>>>>>>>> setLogBinary & setLogMultipart for them):
>>>>>>>>> factory.getOutInterceptors().add(new
>>>>>>>>> LoggingOutInterceptor());
>>>>>>>>> factory.getInInterceptors().add(new
>>>>>>>>> LoggingInInterceptor());
>>>>>>>>> Or feature:
>>>>>>>>> LoggingFeature feature = new LoggingFeature();
>>>>>>>>> feature.setLogMultipart(true);
>>>>>>>>> feature.setLogBinary(true);
>>>>>>>>> ...
>>>>>>>>> Hope it helps, thanks!
>>>>>>>>> Best Regards,
>>>>>>>>> Andriy Redko
>>>>>>>>>> Hi Andriy,
>>>>>>>>>> Thanks for the swift response, but I could still use some
>>>>>>>>> clarifications on:
>>>>>>>>>> 1) You mention that passing an Attachment object as service
>> method
>>>>>>>>>> parameter should work.
>>>>>>>>>> My initial test setup did pass an Attachment object as input
>>>>>>>>>> parameter
>>>>>>>>>>> 1)API interface declaration" in my mail. However when the
>>>>>>>>>> client (see code below) tries to send a request with this
>>>>>>>>>> signature, the
>>>>>>>>>> JAXRSUtils.writeMessageBody(...) method that is called by the
> CXF
>>>>>>>>>> stack throws an exception on the Attachment parameter saying:
>>>>>>>>>> okt 03, 2024 9:46:54 AM
>>>>>>>>>> org.apache.cxf.jaxrs.provider.MultipartProvider
>>>>>>>>>> getHandlerForObject SEVERE: No message body writer found for
>> class
>>>>>>>>>> : class org.apache.cxf.jaxrs.ext.multipart.Attachment.
>>>>>>>>>> okt 03, 2024 9:47:05 AM
>>>>>>>>>> org.apache.cxf.jaxrs.utils.JAXRSUtils
>>>>>>>>>> logMessageHandlerProblem SEVERE: Problem with writing the data,
>>>>>>>>>> class java.util.ArrayList, ContentType: multipart/form-data
>>>>>>>>>> okt 03, 2024 9:47:14 AM
>>>>>>>>>> org.apache.cxf.phase.PhaseInterceptorChain
>>>>>>>>>> doDefaultLogging WARNING: Interceptor for
>>>>>>>>>> {http://api.documenten.magda.common.aeo.dvtm.be/}MessagesApi has
>>>>>>>>>> thrown exception, unwinding now
>>>>>>>>>> org.apache.cxf.interceptor.Fault: Problem with
>>>>>>>>>> writing the data, class java.util.ArrayList, ContentType:
>>>>>>>>> multipart/form-data
>>>>>>>>>> at
> org.apache.cxf.jaxrs.client.ClientProxyImpl$BodyWriter.doWriteBody(ClientP
>>>>>>>> roxyImpl.java:1142)
>>>>>>>>>> at
>>> org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handl
>>>>>>>>>> eMessage(AbstractClient.java:1223)
>>>>>>>>>> The JAXRSUtils.writeMessageBody(...) method takes an 'entity'
>>>>>>>>>> Object that is a List<Attachment>. The first Attachment in the
>>> list
>>>>>>>>>> contains an object of type MessageToSend, while the second one
>>>>>>>>>> contains an object of type Attachment for which 'no message body
>>>>>>>>> writer' could be found.
>>>>>>>>>> The stack creates itself an Attachment object for each parameter
>>> of
>>>>>>>>>> the multipart body, that is why I though that I can not pass it
>> as
>>>>>>>>>> a parameter to my service method. I guess I am not allowed to
>>>>>>>> annotate
>>>>>>>>> an 'Attachment'
>>>>>>>>>> parameter with @Multipart annotation as currently done in the
>>>>>>>>>> method
>>>>>>>>>> signature:
>>>>>>>>>> @FormDataParam(value="upfile1") @Parameter(schema =
>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>> "upfile1", type="application/pdf", required = false) Attachment
>>>>>>>>>> upfile1Detail
>>>>>>>>>> However leaving the @Multipart annotation for the Attachment
>>>>>>>>>> parameter away leads to the error:
>>>>>>>>>> javax.ws.rs.ProcessingException: Resource method
>> be.dvtm.aeo.common.magda.documenten.api.MessagesApi.createMessage2
>>>>>>>>>> has more than one parameter representing a request body
>>>>>>>>>> I.e. now it is no longer clear that the Attachment parameter is
>>>>>>>>>> part of the 'multipart'. That's why I switched to using an
>>>>>>>>>> InputStream as parameter with @Multipart annotation but then I
>>>> loose
>>>>>>>>> the Content-Disposition information.
>>>>>>>>>> The @Multipart annotation doesn't allow to specify
>>>>>>>>>> Content-Disposition information. Is there an alternative here?
>>>>>>>>>> 2) Logging of Binary Data. I create my client with:
>>>>>>>>>> private static MessagesApi getThreadsafeProxy(String
>>>>>>>>> baseAddress) {
>>>>>>>>>> JacksonJsonProvider jjProvider = new
>>>>>>>>>> JacksonJsonProvider(new CustomObjectMapper());
>>>>>>>>>> List<Object> providers = Arrays.asList(new
>>>>>>>>>> MultipartProvider(), jjProvider);
>>>>>>>>>> final JAXRSClientFactoryBean factory = new
>>>>>>>>> JAXRSClientFactoryBean();
>>>>>>>>>> factory.setAddress(baseAddress);
>>>>>>>>>> factory.setServiceClass(MessagesApi.class);
>>>>>>>>>> factory.setProviders(providers);
>>>>>>>>>> factory.getOutInterceptors().add(new
>>>>>>>>> LoggingOutInterceptor());
>>>>>>>>>> factory.getInInterceptors().add(new
>>>>>>>>> LoggingInInterceptor());
>>>>>>>>>> factory.setThreadSafe(true);
>>>>>>>>>> LoggingFeature feature = new LoggingFeature();
>>>>>>>>>> feature.setLogMultipart(true);
>>>>>>>>>> feature.setLogBinary(true);
>> feature.addBinaryContentMediaTypes(MediaType.APPLICATION_OCTET_STREAM);
>>>>>>>> feature.addBinaryContentMediaTypes("application/pdf");
>>>>>>>>>> factory.setFeatures(Arrays.asList(feature));
>>>>>>>>>> MessagesApi api =
>>>> factory.create(MessagesApi.class);
>>>>>>>>>> ClientConfiguration config =
>>>>>>>> WebClient.getConfig(api);
>>>>>>>>>> addTLSClientParameters(config.getHttpConduit());
>>>>>>>>>> return api;
>>>>>>>>>> }
>>>>>>>>>> Here I do activate the logging for multipart and binary and
> also
>>>>>>>>>> added some mediatypes (although couldn't find what it actually
>>>>>>>>>> does). So I was expecting to see the whole message (attachments
>>> are
>>>>>>>>>> rather small as it is a test).
>>>>>>>>>> Well I used wireshark to get the full message and it doesn't
>> show
>>>>>>>>>> any Content-Disposition headers for the multipart elements.
>>>>>>>>>> Regards,
>>>>>>>>>> J.P. Urkens
>>>>>>>>>> -----Original Message-----
>>>>>>>>>> From: Andriy Redko <[email protected]>
>>>>>>>>>> Sent: donderdag 3 oktober 2024 1:01
>>>>>>>>>> To: Jean Pierre URKENS <[email protected]>;
>>>>>>>>>> [email protected]
>>>>>>>>>> Subject: Re: CXF JAX-RS: working with multipart form-data
>>>>>>>>>> Hi Jean,
>>>>>>>>>>> What is the correct way to annotate a file attachment as part
> of
>>> a
>>>>>>>>>>> mutlipart/form-data content body?
>>>>>>>>>> We have many examples in our test suites over here [1], it
> really
>>>>>>>>>> depends on the problem at hand.
>>>>>>>>>>> -> Question-01: Is this the appropriate way to pass
>> files
>>>>>>>>>>> (PDF documents) as attachment in a mutlipart/form-data request,
>>> or
>>>>>>>>>>> are
>>>>>>>>>> there better ways?
>>>>>>>>>> You would probably better of with "multipart/mixed" [2] as in
> you
>>>>>>>>>> case, you are sending different data types. But
>>>>>>>>>> "multipart/form-data" should be fine as well. The right answer
>>>>>>>>>> answer depends on how large the files are. In many cases you are
>>>>>>>>>> better off using chunked transfer (no need to read the whole
> file
>>>> in
>>>>>>>>> memory), like you do with InputStream.
>>>>>>>>>>> -> Question-02: When I activate logging, I can't see
>>>>>>>>>>> anything about the file attachment, not is content, nor the
>>>>>>>>>>> appropriate Content-Disposition settings? I get '--Content
>>>>>>>>>> suppressed--', e.g.:
>>>>>>>>>> Correct, by default the logging interceptors do not log binary
>>>>>>>>>> data, you could checks the docs here [3].
>>>>>>>>>>> -> Question-03: Don't I need to set anything regarding
>>> the
>>>>>>>>>>> Content-Disposition header? The example here is simplified in a
>>>>>>>>>>> sense that I used a test setup with just one file attachment.
> In
>>>>>>>>>>> the real interface up to
>>>>>>>>>>> 20 attachments can be passed. Somehow I need to know which part
>>>>>>>>>>> relates
>>>>>>>>>> to
>>>>>>>>>>> which attachment or not?
>>>>>>>>>> This is probably caused by the fact you are sending the
>>> InputStream
>>>>>>>>>> and not an Attachment, if my understanding is correct. I am
>> pretty
>>>>>>>>>> sure passing the Attachment (as you did initially) should work.
>>>>>>>>>>> -> Question-04: At the server implemtation side, since
>>>>>>>>>>> upfile1Detail is passed as a method parameter, who is going to
>>>>>>>>>>> close this InputStream at the server side?
>>>>>>>>>> The stream should be closed by the service implementation (since
>>>>>>>>>> the stream is expected to be consumed). The Apache CXF runtime
>>> will
>>>>>>>>>> try to close the stream in most cases as well but sometimes it
>> may
>>>>>>>> not
>>>>>>>>> be able to.
>>>>>>>>>> Hope it answers your questions. Thanks!
>>>>>>>>>> [1]
>>> https://github.com/apache/cxf/blob/main/systests/jaxrs/src/test/jav
>>>>>>>>>> a/org/apache/cxf/systest/jaxrs/MultipartStore.java
>>>>>>>>>> [2]
>>> https://learn.microsoft.com/en-us/exchange/troubleshoot/administrat
>>>>>>>>>> ion/multipart-mixed-mime-message-format
>>>>>>>>>> [3] https://cxf.apache.org/docs/message-logging.html
>>>>>>>>>> Best Regards,
>>>>>>>>>> Andriy Redko
>>>>>>>>>>> Hi Andriy,
>>>>>>>>>>> What is the correct way to annotate a file attachment as part
> of
>>> a
>>>>>>>>>>> mutlipart/form-data content body?
>>>>>>>>>>> I currently have the following (only the relevant parts):
>>>>>>>>>>> 1)API interface declaration
>>>>>>>>>>> ======================
>>>>>>>>>>> @POST
>>>>>>>>>>> @Path("/messages1")
>>>>>>>>>>> @Consumes("multipart/form-data")
>>>>>>>>>>> @Produces({ "application/json" })
>>>>>>>>>>> @Operation(...)
>>>>>>>>>>> @ApiResponses(...)
>>>>>>>>>>> Response createMessage1(
>>>>>>>>>>> @HeaderParam("x-correlation-id")
>> @NotNull
>>>>>>>>>>> @Size(min = 10, max = 36) @Parameter(description="ID of the
>>>>>>>>>>> transaction. Use this ID for log tracing and incident
>> handling.")
>>>>>>>>>> String xCorrelationId,
>>>>>>>>>>> @HeaderParam("Idempotency-Key")
> @NotNull
>>>>>>>>>>> @Size(min = 10, max = 36) @Parameter(description="When retrying
>> a
>>>>>>>>>>> failed call, the retry call should have the same Idempotency
>>>>>>>>>>> Key.")
>>>>>>>>>> String idempotencyKey,
>>>>>>>>>>> @FormDataParam(value="messageToSend")
>>>>>>>>>>> @Parameter(required=true,schema =
>>>>>>>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value =
>>>>>>>>>>> "messageToSend", type="application/json", required= true)
>>>>>>>>>>> MessageToSend messageToSend,
>>>>>>>>>>> @FormDataParam(value="upfile1")
>>>>>>>>>>> @Parameter(schema = @Schema(type = "string", format =
> "binary"))
>>>>>>>>>>> @Multipart(value = "upfile1", type="application/octet-stream",
>>>>>>>>>>> required = false) Attachment upfile1Detail);
>>>>>>>>>>> So I pass the file to upload as an Attachment object.
>>>>>>>>>>> 2)API client test code
>>>>>>>>>>> =================
>>>>>>>>>>> String xCorrelationId =
>>>>>>>> UUID.randomUUID().toString();
>>>>>>>>>>> String idempotencyKey =
>>>>>>>>>>> UUID.randomUUID().toString();
>>>>>>>>>>> //01. Get the attachment to include
>>>>>>>>>>> String fileName = "test.pdf";
>>>>>>>>>>> try (InputStream is =
>>>>>>>>>>> this.getClass().getClassLoader().getResourceAsStream(fileName);
>>>>>>>>>>> BufferedReader reader = new
>>>>>>>>>>> BufferedReader(new InputStreamReader(is))) {
>>>>>>>>>>> if (is == null) {
>>>>>>>>>>> Assert.fail("Couldn't load
>>>>>>>>>>> test.pdf
>>>>>>>>>> from classpath!");
>>>>>>>>>>> }
>>>>>>>>>>> DataHandler dataHandler = new
>>>>>>>>>>> DataHandler(is,
>>>>>>>>>> "application/pdf");
>>>>>>>>>>> ContentDisposition cd = new
>> ContentDisposition("attachment;name=upfile1;filename="+fileName);
>>>>>>>>>>> Attachment upfile1Detail = new
>>>>>>>>>> AttachmentBuilder()
>>>>>>>>>>> .id("upfile1")
>>>>>>>>>>> .dataHandler(dataHandler)
>>>>>>>>>>> .contentDisposition(cd)
>>>>>>>>>>> .mediaType("application/pdf")
>>>>>>>>>>> .build();
>>>>>>>>>>> //02. create the message to send
>>>>>>>>>>> MessageToSend mts = new MessageToSend();
>>>>>>>>>>> //03. Call the server
>>>>>>>>>>> Response resp =
>>>>>>>>>>> apiClient.createMessage1(xCorrelationId, idempotencyKey, mts,
>>>>>>>>>>> upfile1Detail);
>>>>>>>>>>> When running the API client test code and getting at '03.' The
>>>>>>>> method:
>>>>>>>>>>> JAXRSUtils.writeMessageBody(List<WriterInterceptor>
>>>>>>>>>>> writers,Object entity,Class<?> type, Type
>>> genericType,Annotation[]
>>>>>>>>>>> annotations,MediaType mediaType,MultivaluedMap<String, Object>
>>>>>>>>>>> httpHeaders,Message message)
>>>>>>>>>>> is called. The 'entity' object is a list of Attachment objects
>>>>>>>> where:
>>>>>>>>>>> - the first Attachment object contains an object of type
>>>>>>>>>>> MessageToSend
>>>>>>>>>>> - the second Attachment object contains an object of type
>>>>>>>>>>> Attachment
>>>>>>>>>>> This second object leads to a fatal error within the
>>>>>>>>>>> JAXRSUtils.writeMessageBody(...) method:
>>>>>>>>>>> okt 02, 2024 1:36:02 PM
>>>>>>>>>>> org.apache.cxf.jaxrs.provider.MultipartProvider
>>>>>>>>>>> getHandlerForObject
>>>>>>>>>>> SEVERE: No message body writer found for class : class
>>>>>>>>>>> org.apache.cxf.jaxrs.ext.multipart.Attachment.
>>>>>>>>>>> okt 02, 2024 1:36:02 PM
>>>>>>>>>>> org.apache.cxf.jaxrs.utils.JAXRSUtils
>>>>>>>>>>> logMessageHandlerProblem
>>>>>>>>>>> SEVERE: Problem with writing the data, class
>>>>>>>>>>> java.util.ArrayList,
>>>>>>>>>>> ContentType: multipart/form-data
>>>>>>>>>>> I modified the interface and implementation classes to use
>>>>>>>>>>> 'InputStream upfile1Detail' as type for the input parameter of
>>> the
>>>>>>>>>>> service method. And in this case my 'dummy' server
>> implementation
>>>>>>>>>>> can read the file and save it to disk.
>>>>>>>>>>> -> Question-01: Is this the appropriate way to pass
>> files
>>>>>>>>>>> (PDF documents) as attachment in a mutlipart/form-data request,
>>> or
>>>>>>>>>>> are
>>>>>>>>>> there better ways?
>>>>>>>>>>> -> Question-02: When I activate logging, I can't see
>>>>>>>>>>> anything about the file attachment, not is content, nor the
>>>>>>>>>>> appropriate Content-Disposition settings? I get '--Content
>>>>>>>>>> suppressed--', e.g.:
>>>>>>>>>>> [MAGDADOC] 2024-10-02 14:29:08,911 [main] INFO
>>>>>>>>>>> $--$
>>>>>>>>>>> (org.apache.cxf.ext.logging.slf4j.Slf4jEventSender:84) -
> REQ_OUT
>>>>>>>>>>> Address:
>> http://localhost:8091/services/magdadoc/api/v1/messages/messages1
>>>>>>>>>>> HttpMethod: POST
>>>>>>>>>>> Content-Type: multipart/form-data;
>>>>>>>>>>> boundary="uuid:3c3fa9c0-8470-4655-b026-3ed09f79e862"
>>>>>>>>>>> ExchangeId:
>>>>>>>> a583a695-d881-4fa7-b65a-8961cdbbd412
>>>>>>>>>>> Headers: {Authorization=Bearer
>>>>>>>>>>> f4262ccf-3250-4bcf-a1bc-7ee1bf9a56cf,
>>>>>>>>>>> Accept=application/json,
>>>>>>>>>>> Idempotency-Key=bd06c05d-9fe2-4b60-b8db-5ad1121b74dc,
>>>>>>>>>>> x-correlation-id=511c51ba-95fe-4f69-9443-f05c377cffab}
>>>>>>>>>>> Payload:
>>>>>>>>>>> --uuid:3c3fa9c0-8470-4655-b026-3ed09f79e862
>>>>>>>>>>> Content-Type: application/json
>>>>>>>>>>> Content-Transfer-Encoding: binary
>>>>>>>>>>> Content-ID: <messageToSend>
>>>>>>>>>>> {
>>>>>>>>>>> "delivery" : "AUTOMATIC",
>>>>>>>>>>> "eboxDeliveryData" : { /* all fine */},
>>>>>>>>>>> "paperDeliveryData" : {/* all fine */},
>>>>>>>>>>> "emailDeliveryData" : null,
>>>>>>>>>>> "businessData" : [ ]
>>>>>>>>>>> }
>>>>>>>>>>> --uuid:3c3fa9c0-8470-4655-b026-3ed09f79e862
>>>>>>>>>>> --- Content suppressed ---
>>>>>>>>>>> -> Question-03: Don't I need to set anything regarding
>>> the
>>>>>>>>>>> Content-Disposition header? The example here is simplified in a
>>>>>>>>>>> sense that I used a test setup with just one file attachment.
> In
>>>>>>>>>>> the real interface up to
>>>>>>>>>>> 20 attachments can be passed. Somehow I need to know which part
>>>>>>>>>>> relates
>>>>>>>>>> to
>>>>>>>>>>> which attachment or not?
>>>>>>>>>>> -> Question-04: At the server implemtation side, since
>>>>>>>>>>> upfile1Detail is passed as a method parameter, who is going to
>>>>>>>>>>> close this InputStream at the server side?
>>>>>>>>>>> Regards,
>>>>>>>>>>> J.P. Urkens
>>>>>>>>>>> P.S.: Is there a migration document describing upgrading
>>> CXF-3.5.6
>>>>>>>>>>> (my current version) to CXF-3.5.8 (latest JDK8 version). I'd
>> like
>>>>>>>>>>> to know whether upgrading can happen without too much burden.
>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>> From: Jean Pierre URKENS <[email protected]>
>>>>>>>>>>> Sent: vrijdag 5 juli 2024 13:04
>>>>>>>>>>> To: 'Andriy Redko' <[email protected]>; '[email protected]'
>>>>>>>>>>> <[email protected]>
>>>>>>>>>>> Subject: RE: CXF JAX-RS: working with multipart form-data
>>>>>>>>>>> Hi Andriy,
>>>>>>>>>>> When searching the net I came along this Jersey annotation but
>>>>>>>>>>> since I was not depending on 'Jersey' components I skipped it.
>> So
>>>>>>>>>>> apparently swagger-core has processing for externally defined
>>>>>>>>>>> annotations (like this FormDataParam in Jersey), indeed inside
>>>>>>>>>> know-how.
>>>>>>>>>>> I included it in my project as
>>>>>>>>>>> io.swagger.v3.oas.annotations.FormDataParam,
>>>>>>>>>>> since it should somehow be included in the supported swagger
>>>>>>>>>>> annotations (maybe we should submit a request for it to the
>>>>>>>>>>> swagger-core team) and this indeed generates an appropriate
>>>>>>>>>> openapi.json spec.
>>>>>>>>>>> Thanks alot,
>>>>>>>>>>> J.P. Urkens
>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>> From: Andriy Redko <[email protected]>
>>>>>>>>>>> Sent: vrijdag 5 juli 2024 2:16
>>>>>>>>>>> To: Jean Pierre URKENS <[email protected]>;
>>>>>>>>>>> [email protected]
>>>>>>>>>>> Subject: Re: CXF JAX-RS: working with multipart form-data
>>>>>>>>>>> Hi Jean,
>>>>>>>>>>> Here is how you could make it work (there is some magic
>> knowledge
>>>>>>>>>>> involved sadly). First of all, define such annotation anywhere
>> in
>>>>>>>>>>> your codebase (where it dims appropriate):
>>>>>>>>>>> import java.lang.annotation.ElementType; import
>>>>>>>>>>> java.lang.annotation.Retention; import
>>>>>>>>>>> java.lang.annotation.RetentionPolicy;
>>>>>>>>>>> import java.lang.annotation.Target;
>>>>>>>>>>> @Target({ElementType.PARAMETER, ElementType.METHOD,
>>>>>>>>>>> ElementType.FIELD})
>>>>>>>>>>> @Retention(RetentionPolicy.RUNTIME)
>>>>>>>>>>> public @interface FormDataParam {
>>>>>>>>>>> String value();
>>>>>>>>>>> }
>>>>>>>>>>> Use this annotation on each Attachment parameter:
>>>>>>>>>>> /* Skipping other annotations as those are not important here
> */
>>>>>>>>>>> public Response createMessage(
>>>>>>>>>>> @HeaderParam("x-correlation-id") @NotNull @Size(min =
>>> 10,
>>>>>>>>>>> max = 36) @Parameter(description="ID of the transaction. Use
>> this
>>>>>>>>>>> ID for log tracing and incident handling.") String
>>> xCorrelationId,
>>>>>>>>>>> @HeaderParam("Idempotency-Key") @Size(min = 10, max =
>>> 36)
>>>>>>>>>>> @Parameter(description="When retrying a failed call, the retry
>>>>>>>>>>> call should have the same Idempotency Key.") String
>>>> idempotencyKey,
>>>>>>>>>>> @FormDataParam("upfile1") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile1", type="application/octet-stream", required = false)
>>>>>>>>>>> InputStream upfile1Detail,
>>>>>>>>>>> @FormDataParam("upfile2") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile2", type="application/octet-stream", required = false)
>>>>>>>>>>> InputStream upfile2Detail,
>>>>>>>>>>> @FormDataParam("upfile3") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile3", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile3Detail,
>>>>>>>>>>> @FormDataParam("upfile4") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile4", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile4Detail,
>>>>>>>>>>> @FormDataParam("upfile5") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile5", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile5Detail,
>>>>>>>>>>> @FormDataParam("upfile6") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile6", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile6Detail,
>>>>>>>>>>> @FormDataParam("upfile7") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile7", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile7Detail,
>>>>>>>>>>> @FormDataParam("upfile8") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile8", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile8Detail,
>>>>>>>>>>> @FormDataParam("upfile9") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile9", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment
>>>>>>>>>> upfile9Detail,
>>>>>>>>>>> @FormDataParam("upfile10") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile10", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile10Detail,
>>>>>>>>>>> @FormDataParam("upfile11") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile11", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile11Detail,
>>>>>>>>>>> @FormDataParam("upfile12") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile12", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile12Detail,
>>>>>>>>>>> @FormDataParam("upfile13") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile13", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile13Detail,
>>>>>>>>>>> @FormDataParam("upfile14") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile14", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile14Detail,
>>>>>>>>>>> @FormDataParam("upfile15") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile15", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile15Detail,
>>>>>>>>>>> @FormDataParam("upfile16") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile16", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile16Detail,
>>>>>>>>>>> @FormDataParam("upfile17") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile17", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile17Detail,
>>>>>>>>>>> @FormDataParam("upfile18") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile18", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile18Detail,
>>>>>>>>>>> @FormDataParam("upfile19") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile19", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile19Detail,
>>>>>>>>>>> @FormDataParam("upfile20") @Parameter(schema =
>>>>>>>>>>> @Schema(type = "string", format = "binary")) @Multipart(value =
>>>>>>>>>>> "upfile20", type="application/octet-stream", required = false)
>>>>>>>>>>> Attachment upfile20Detail,
>>>>>>>>>>> @FormDataParam("qrfile") @Parameter(schema =
>>> @Schema(type
>>>>>>>>>>> = "string", format = "binary")) @Multipart(value = "qrfile",
>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>> qrfileDetail
>>>>>>>>>>> ) {
>>>>>>>>>>> ....
>>>>>>>>>>> }
>>>>>>>>>>> With that, you will get a nice request body schema (publishing
> a
>>>>>>>>>>> bit large YAML snippet to preserve the context):
>>>>>>>>>>> paths:
>>>>>>>>>>> /sample/messages:
>>>>>>>>>>> post:
>>>>>>>>>>> tags:
>>>>>>>>>>> - messages
>>>>>>>>>>> summary: "Send a message, using a channel (email, paper
>>>>>>>>>>> mail,
>>>>>>>>>>> ebox) and delivery\
>>>>>>>>>>> \ method (registered or normal) of your choice. More
>> than
>>>>>>>>>>> 6 upfiles only supported\
>>>>>>>>>>> \ for PAPER delivery."
>>>>>>>>>>> operationId: createMessage
>>>>>>>>>>> parameters:
>>>>>>>>>>> - name: x-correlation-id
>>>>>>>>>>> in: header
>>>>>>>>>>> description: ID of the transaction. Use this ID for log
>>>>>>>>>>> tracing and incident
>>>>>>>>>>> handling.
>>>>>>>>>>> required: true
>>>>>>>>>>> schema:
>>>>>>>>>>> maxLength: 36
>>>>>>>>>>> minLength: 10
>>>>>>>>>>> type: string
>>>>>>>>>>> - name: Idempotency-Key
>>>>>>>>>>> in: header
>>>>>>>>>>> description: "When retrying a failed call, the retry
>> call
>>>>>>>>>>> should have the\
>>>>>>>>>>> \ same Idempotency Key."
>>>>>>>>>>> schema:
>>>>>>>>>>> maxLength: 36
>>>>>>>>>>> minLength: 10
>>>>>>>>>>> type: string
>>>>>>>>>>> requestBody:
>>>>>>>>>>> content:
>>>>>>>>>>> multipart/form-data:
>>>>>>>>>>> schema:
>>>>>>>>>>> type: object
>>>>>>>>>>> properties:
>>>>>>>>>>> upfile1:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile2:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile3:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile4:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile5:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile6:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile7:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile8:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile9:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile10:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile11:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile12:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile13:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile14:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile15:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile16:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile17:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile18:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile19:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> upfile20:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> qrfile:
>>>>>>>>>>> type: string
>>>>>>>>>>> format: binary
>>>>>>>>>>> The key here is @FormDataParam annotation which (originally)
>>> comes
>>>>>>>>>>> from Jersey but has special treatment in Swagger Core (but,
>>>>>>>>>>> likely, no attribution to Jersey).
>>>>>>>>>>> Hope it helps!
>>>>>>>>>>> Thank you.
>>>>>>>>>>> Best Regards,
>>>>>>>>>>> Andriy Redko
>>>>>>>>>>>> V2.2.22 (15/05/2024) is the latest version of
>> io.swagger.core.v3
>>>>>>>>>>>> libraries.
>>>>>>>>>>>> I upgrade to this version to make sure I had the latest
>> swagger
>>>>>>>>>>>> implementation.
>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>> From: Andriy Redko <[email protected]>
>>>>>>>>>>>> Sent: donderdag 4 juli 2024 4:44
>>>>>>>>>>>> To: Jean Pierre URKENS <[email protected]>;
>>>>>>>>>>>> [email protected]
>>>>>>>>>>>> Subject: Re: CXF JAX-RS: working with multipart form-data
>>>>>>>>>>>> Hi Jean,
>>>>>>>>>>>> Interesting, I was experimenting with different ways to
> express
>>>>>> what
>>>>>>>>>>>> you need, but no luck so far, I will try to spend a bit more
>>> time
>>>>>> on
>>>>>>>>>>>> that this week since OAS 3.x does support multipart [1] but we
>>>> may
>>>>>>>>>>>> indeed hit the
>>>>>>>>>>>> limitation(s) of this particular Swagger Core version. Thank
>>> you.
>>>>>>>>>>>> [1]
>>>>>> https://swagger.io/docs/specification/describing-request-body/multip
>>>>>>>>>>>> a
>>>>>>>>>>>> r
>>>>>>>>>>>> t-requests/
>>>>>>>>>>>> Best Regards,
>>>>>>>>>>>> Andriy Redko
>>>>>>>>>>>>> Hi Andriy,
>>>>>>>>>>>>> I already tried this but it didn't work. E.g. for following
>> API
>>>>>>>>>>>>> interface
>>>>>>>>>>>>> specification:
>>>>>>>>>>>>> /**
>>>>>>>>>>>>> * Send a message, using a channel (email,
>>>>>> paper
>>>>>>>>>>>>> mail,
>>>>>>>>>>>>> ebox) and delivery method (registered or normal) of your
>>> choice.
>>>>>>>>>>>>> More than
>>>>>>>>>>>>> 6 upfiles only supported for PAPER delivery.
>>>>>>>>>>>>> *
>>>>>>>>>>>>> */
>>>>>>>>>>>>> @POST
>>>>>>>>>>>>> @Path("/messages")
>>>>>>>>>>>>> @Consumes("multipart/form-data")
>>>>>>>>>>>>> @Produces({ "application/json" })
>>>>>>>>>>>>> @Operation(
>>>>>> summary
>>>>>>>>>>>>> = "Send a message, using a channel (email, paper mail, ebox)
>>> and
>>>>>>>>>>>>> delivery method (registered or normal) of your choice. More
>>> than
>>>>> 6
>>>>>>>>>>>>> upfiles only supported for PAPER delivery.",
>>> tags
>>>>> =
>>>>>>>>>>>>> {"messages" }, operationId="createMessage",
>>>>>>>>>>>>> security=@SecurityRequirement(name="BearerAuthentication"))
>>>>>>>>>>>>> @ApiResponses({ @ApiResponse(
> responseCode
>>> =
>>>>>>>>>>>>> "201", description = "Created", content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,array=@ArraySchema(sc
>>>>>>>>>>>>> h e m a=@Schema(implementation=SendStatusMessage.class))),
>>>>>>>>>>>>> headers = {@Header(
>>>>>>>>>>>>> name="X-Magda-Exceptions",
>>>>>>>>>>>>> required=false,
>>>>>>>>>>>>> description="Only used in the context of EBOX delivery and if
>>>>>> there
>>>>>>>>>>>>> was a problem with the consent of the receiver's ebox.",
>>>>>>>>>>>>> schema=@Schema(implementation=MagdaExceptionList.class))
>>>>>>>>>>>>> }),
>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>> responseCode = "400",
>>>>>>>>>>>>> description = "Invalid data supplied", content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>>>>>>>> e
>>>>>>>>>>>>> n
>>>>>>>>>>>>> t
>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>> responseCode = "401",
>>>>>>>>>>>>> description = "Invalid authorization", content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>>>>>>>> e
>>>>>>>>>>>>> n
>>>>>>>>>>>>> t
>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>> responseCode = "500",
>>>>>>>>>>>>> description = "Unexpected Server Error", content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>>>>>>>> e
>>>>>>>>>>>>> n
>>>>>>>>>>>>> t
>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>> responseCode = "502",
>>>>>>>>>>>>> description = "Bad Gateway",
>>>>>>>>>>>>> content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>>>>>>>> e
>>>>>>>>>>>>> n
>>>>>>>>>>>>> t
>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>> responseCode = "503",
>>>>>>>>>>>>> description = "Service unavailable", content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>>>>>>>> e
>>>>>>>>>>>>> n
>>>>>>>>>>>>> t
>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>> responseCode = "504",
>>>>>>>>>>>>> description = "Gateway Timeout",
>>>>>>>>>>>>> content =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(implem
>>>>>>>>>>>>> e
>>>>>>>>>>>>> n
>>>>>>>>>>>>> t
>>>>>>>>>>>>> ation=ErrorMessage.class)))
>>>>>>>>>>>>> })
>>>>>>>>>>>>> public Response createMessage(
>>>>>>>>>>>>> @HeaderParam("x-correlation-id") @NotNull @Size(min = 10, max
>> =
>>>>>> 36)
>>>>>>>>>>>>> @Parameter(description="ID of the transaction. Use this ID
> for
>>>>> log
>>>>>>>>>>>>> tracing and incident handling.") String xCorrelationId,
>>>>>>>>>>>>> @HeaderParam("Idempotency-Key") @Size(min = 10, max = 36)
>>>>>>>>>>>>> @Parameter(description="When retrying a failed call, the
> retry
>>>>>> call
>>>>>>>>>>>>> should have the same Idempotency Key.") String
> idempotencyKey,
>>>>>>>>>>>>> @Parameter(required=true,schema =
>>>>>>>>>>>>> @Schema(implementation=MessageToSend.class)) @Multipart(value
>> =
>>>>>>>>>>>>> "messageToSend", type="application/json", required= true)
>>>>>>>>>>>>> MessageToSend messageToSend, @Parameter(schema = @Schema(type
>> =
>>>>>>>>>>>>> "string", format = "binary")) @Multipart(value = "upfile1",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile1Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile2",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile2Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile3",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile3Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile4",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile4Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile5",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile5Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile6",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile6Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile7",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile7Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile8",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile8Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile9",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile9Detail, @Parameter(schema = @Schema(type = "string",
>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile10",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile10Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile11",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile11Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile12",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile12Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile13",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile13Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile14",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile14Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile15",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile15Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile16",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile16Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile17",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile17Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile18",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile18Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile19",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile19Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "upfile20",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> upfile20Detail, @Parameter(schema = @Schema(type = "string",
>>>>>> format
>>>>>>>>>>>>> =
>>>>>>>>>>>>> "binary")) @Multipart(value = "qrfile",
>>>>>>>>>>>>> type="application/octet-stream", required = false) Attachment
>>>>>>>>>>>>> qrfileDetail); I've attached the generated openapi
>>>> specification.
>>>>>>>>>>>>> It only contains the 'messageToSend' as part of the
>>>>>>>>>>>>> multipart/form-data requestBody content, all attachments are
>>>>>>>> ignored.
>>>>>>>>>>>>> Below I've listed the libraries I've included in the project
>>>> (cxf
>>>>>>>>>>>>> v3.5.8 and swagger v2.2.2). Which of these libraries is
> acutal
>>>>>>>>>>>>> responsible for generating the openapi.json specification
> from
>>>>> the
>>>>>>>>>>>>> interface description?
>>>>>>>>>>>>> * cxf-rt-rs-service-description-common-openapi:3.5.8
>>>>>>>>>>>>> * cxf-rt-rs-service-description-openapi:3.5.8
>>>>>>>>>>>>> * cxf-rt-rs-service-description-swagger-ui:3.5.8
>>>>>>>>>>>>> * swagger-core:2.2.2
>>>>>>>>>>>>> * swagger-annotations:2.2.2
>>>>>>>>>>>>> * swagger-integration:2.2.2
>>>>>>>>>>>>> * swagger-jaxrs2: 2.2.2
>>>>>>>>>>>>> * swagger-model: 2.2.2
>>>>>>>>>>>>> Note that I am still on JDK8, so I guess I can't upgrade to a
>>>>>>>>>>>>> higher version (currently our projects use cxf-v3.5.6 and
>>>> swagger
>>>>>>>>>>>>> 2.1.13).
>>>>>>>>>>>>> Regards,
>>>>>>>>>>>>> J.P. Urkens
>>>>>>>>>>>>> -----Original Message-----
>>>>>>>>>>>>> From: Andriy Redko <[email protected]>
>>>>>>>>>>>>> Sent: woensdag 3 juli 2024 5:57
>>>>>>>>>>>>> To: Jean Pierre URKENS <[email protected]>;
>>>>>>>>>>>>> [email protected]
>>>>>>>>>>>>> Subject: Re: CXF JAX-RS: working with multipart form-data Hi
>>>> Jean
>>>>>>>>>>>>> Pierre, I suspect the @Multipart annotation is coming from
> CXF
>>>>>>>>>>>>> (org.apache.cxf.jaxrs.ext.multipart.Multipart), right? If
> yes,
>>>>>> this
>>>>>>>>>>>>> is not a part of JAX-RS specification but CXF specific
>>>> extension.
>>>>>>>>>>>>> You may need to add Swagger API annotation to the parameters
>> in
>>>>>>>>>>>>> question:
>>>>>>>>>>>>> @Parameter(schema = @Schema(type = "string", format =
>>>>>> "binary"))
>>>>>>>>>>>>> Hope it helps.
>>>>>>>>>>>>> Thank you.
>>>>>>>>>>>>> Best Regards,
>>>>>>>>>>>>> Andriy Redko
>>>>>>>>>>>>> Monday, July 1, 2024, 12:09:17 PM, you wrote:
>>>>>>>>>>>>>> Hi all,
>>>>>>>>>>>>>> I am having problems to correctly annotate service methods
>>>> which
>>>>>>>>>>>>>> consumes multipart/form-data that contains attachments next
>> to
>>>>>>>>>>>>>> other model objects.
>>>>>>>>>>>>>> I've an openapi specification that contains following
>>>>> requestBody
>>>>>>>>>>>>>> definition:
>>>>>>>>>>>>>> /messages:
>>>>>>>>>>>>>> post:
>>>>>>>>>>>>>> tags:
>>>>>>>>>>>>>> - "messages"
>>>>>>>>>>>>>> summary: "Send a message, using a channel (email,
> paper
>>>>>>>>>>>>>> mail,
>>>>>>>>>>>>>> ebox) and delivery method (registered or normal) of your
>>>> choice.
>>>>>>>>>>>>>> More than 6 upfiles only supported for PAPER delivery."
>>>>>>>>>>>>>> operationId: createMessage
>>>>>>>>>>>>>> parameters:
>>>>>>>>>>>>>> - $ref: '#/components/parameters/CorrelationId'
>>>>>>>>>>>>>> - $ref: '#/components/parameters/Idempotency-Key'
>>>>>>>>>>>>>> requestBody:
>>>>>>>>>>>>>> content:
>>>>>>>>>>>>>> multipart/form-data:
>>>>>>>>>>>>>> schema:
>>>>>>>>>>>>>> type: object
>>>>>>>>>>>>>> required:
>>>>>>>>>>>>>> - messageToSend
>>>>>>>>>>>>>> properties:
>>>>>>>>>>>>>> messageToSend:
>>>>>>>>>>>>>> $ref: '#/components/schemas/MessageToSend'
>>>>>>>>>>>>>> upfile1:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile2:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile3:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile4:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile5:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile6:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile7:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile8:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile9:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile10:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile11:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile12:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile13:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile14:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile15:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile16:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile17:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile18:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile19:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> upfile20:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> qrfile:
>>>>>>>>>>>>>> type: string
>>>>>>>>>>>>>> format: binary
>>>>>>>>>>>>>> nullable: true
>>>>>>>>>>>>>> required: true
>>>>>>>>>>>>>> When using the openapi-generator-maven-plugin v7.6.0 it
>>>>> generates
>>>>>>>>>>>>>> following method signature:
>>>>>>>>>>>>>> @POST
>>>>>>>>>>>>>> @Path("/messages")
>>>>>>>>>>>>>> @Consumes("multipart/form-data")
>>>>>>>>>>>>>> @Produces({ "application/json" })
>>>>>>>>>>>>>> @Operation(
>>>>>>>>>>>>>> summary = "Send a message, using a
>>>>>> channel
>>>>>>>>>>>>>> (email, paper mail, ebox) and delivery method (registered or
>>>>>>>>>>>>>> normal) of your choice. More than 6 upfiles only supported
>> for
>>>>>>>>>>>>>> PAPER delivery.",
>>>>>>>>>>>>>> tags = {"messages" },
>>>>>>>>>>>>>> operationId="createMessage",
>>>>>>>>>>>>>> security=@SecurityRequirement(name="BearerAuthentication"),
>>>>>>>>>>>>>> responses= {
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "201",
>>>>>>>>>>>>>> description = "Created",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,array=@ArraySchema(s
>>>>>>>>>>>>>> c h em a=@Schema(implementation=SendStatusMessage.class))),
>>> headers
>>>>> =
>>>>>>>>>>>>>> {@Header( name="X-Magda-Exceptions", required=false,
>>>>>>>>>>>>>> description="Only used in the context of EBOX delivery and
> if
>>>>>>>>>>>>>> there was a problem with the consent of the receiver's
>> ebox.",
>>>>>>>>>>>>>> schema=@Schema(implementation=MagdaExceptionList.class))
>>>>>>>>>>>>>> }),
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "400",
>>>>>>>>>>>>>> description = "Invalid data supplied",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>>>>>>>> m
>>>>>>>>>>>>>> e
>>>>>>>>>>>>>> nt
>>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "401",
>>>>>>>>>>>>>> description = "Invalid authorization",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>>>>>>>> m
>>>>>>>>>>>>>> e
>>>>>>>>>>>>>> nt
>>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "500",
>>>>>>>>>>>>>> description = "Unexpected Server Error",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>>>>>>>> m
>>>>>>>>>>>>>> e
>>>>>>>>>>>>>> nt
>>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "502",
>>>>>>>>>>>>>> description = "Bad Gateway",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>>>>>>>> m
>>>>>>>>>>>>>> e
>>>>>>>>>>>>>> nt
>>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "503",
>>>>>>>>>>>>>> description = "Service unavailable",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>>>>>>>> m
>>>>>>>>>>>>>> e
>>>>>>>>>>>>>> nt
>>>>>>>>>>>>>> ation=ErrorMessage.class))),
>>>>>>>>>>>>>> @ApiResponse(
>>>>>>>>>>>>>> responseCode = "504",
>>>>>>>>>>>>>> description = "Gateway Timeout",
>>> content
>>>>> =
>>>>>> @Content(mediaType=MediaType.APPLICATION_JSON,schema=@Schema(imple
>>>>>>>>>>>>>> m
>>>>>>>>>>>>>> e
>>>>>>>>>>>>>> nt
>>>>>>>>>>>>>> ation=ErrorMessage.class)))
>>>>>>>>>>>>>> })
>>>>>>>>>>>>>> public Response createMessage(
>>>>>>>>>>>>>> @HeaderParam("x-correlation-id")
>>>>> @NotNull
>>>>>>>>>>>>>> @Size(min = 10, max = 36) @Parameter(description="ID of the
>>>>>>>>>>>>>> transaction. Use this ID for log tracing and incident
>>>>> handling.")
>>>>>>>>>>>>>> String xCorrelationId,
>>>>>>>>>>>>>> @HeaderParam("Idempotency-Key")
>>>>> @Size(min
>>>>>>>>>>>>>> = 10, max = 36) @Parameter(description="When retrying a
>> failed
>>>>>>>>>>>>>> call, the retry call should have the same Idempotency Key.")
>>>>>>>>>>>>>> String idempotencyKey,
>>>>>>>>>>>>>> @Multipart(value = "messageToSend",
>>>>>>>>>>>>>> required=
>>>>>>>>>>>>>> true) MessageToSend messageToSend,
>>>>>>>>>>>>>> @Multipart(value = "upfile1",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile1Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile2",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile2Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile3",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile3Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile4",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile4Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile5",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile5Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile6",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile6Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile7",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile7Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile8",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile8Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile9",
>> required
>>>> =
>>>>>>>>>>>>>> false) Attachment upfile9Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile10",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile10Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile11",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile11Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile12",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile12Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile13",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile13Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile14",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile14Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile15",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile15Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile16",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile16Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile17",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile17Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile18",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile18Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile19",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile19Detail,
>>>>>>>>>>>>>> @Multipart(value = "upfile20",
>>> required
>>>>> =
>>>>>>>>>>>>>> false) Attachment upfile20Detail,
>>>>>>>>>>>>>> @Multipart(value = "qrfile",
> required
>>> =
>>>>>>>>>>>>>> false) Attachment qrfileDetail); If I now generate the
>>> swagger
>>>>>>>>>>>>>> from this code (I modified the annotations in the generated
>>>> code
>>>>>>>>>>>>>> for using OAS v3 annotations through swagger-jaxrs2 v2.1.13
>>> and
>>>>> I
>>>>>>>>>>>>>> am using cxf-v3.5.6 having swagger-ui v4.18.2 generate the
>>> user
>>>>>>>>>>>>>> interface) none of the upload files appears as request
>>>>> parameter,
>>>>>>>>>>>>>> only the messageToSend is shown.
>>>>>>>>>>>>>> Is the above signature for the method createMessage(...)
>>>>>>> incorrect?
>>>>>>>>>>>>>> If I look at the generated openapi.json all the Attachment
>>>>>> upFiles
>>>>>>>>>>>>>> are missing from the specification? So is it a
>>>>>>>>>>>>>> problem/short-coming(?) of the used software libraries,
> which
>>>>>>> then:
>>>>>>>>>>>>>> . cxf-rt-rs-service-description-common-openapi v3.5.6
>>>>>>>>>>>>>> this library references swagger-jaxrs2 v2.1.13 .
>>>>>>>> swagger-jaxrs2
>>>>>>>>>>>>>> v2.1.13 -> can I
>>>>>>>> upgradethis
>>>>>>>>>>>>>> to
>>>>>>>>>>>>>> e.g. swagger-jaxrs2 v2.2.22 (latest) while retaining cxf
>>>> v3.5.6?
>>>>>>>>>>>>>> . ...another?
>>>>>>>>>>>>>> Regards,
>>>>>>>>>>>>>> J.P.