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.

Reply via email to