Hi Jean, > The option to use a List<Attachment> or a MultipartBody does work, I've > testcases to confirm this.
Correct, what I meant is if could keep the API contract as: Response api(Attachment a1, Attachment a2, Attachment a3) But (internally only) fold it to: Response api(List<Attachment> atts) So you would preserve all the API contract metadata, as you have it now, however CXF would go one step further to support it behind the scene. I think that is what you are looking for, no need to tinker with @Multipart otherwise, is that the case? > Strangely I don't even find a reference to the header Content-ID in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers. Content-ID is not a HTTP header but "cid:" from https://www.rfc-editor.org/rfc/rfc2392.txt, as you also referred to. Thanks! 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 code->>>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 <drr...@gmail.com> > Verzonden: vrijdag 15 november 2024 21:02 > Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; > dev@cxf.apache.org > 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 <jean-pierre.urk...@devoteam.com> >> Verzonden: donderdag 14 november 2024 11:29 >> Aan: 'Andriy Redko' <drr...@gmail.com>; 'dev@cxf.apache.org' >> <dev@cxf.apache.org> >> 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 <drr...@gmail.com> >> Verzonden: woensdag 13 november 2024 23:42 >> Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >> dev@cxf.apache.org >> 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 <drr...@gmail.com> >>> Verzonden: vrijdag 4 oktober 2024 2:52 >>> Aan: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >>> dev@cxf.apache.org >>> 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 <drr...@gmail.com> >>>> Sent: donderdag 3 oktober 2024 1:01 >>>> To: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >>>> dev@cxf.apache.org >>>> 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 <jean-pierre.urk...@devoteam.com> >>>>> Sent: vrijdag 5 juli 2024 13:04 >>>>> To: 'Andriy Redko' <drr...@gmail.com>; 'dev@cxf.apache.org' >>>>> <dev@cxf.apache.org> >>>>> 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 <drr...@gmail.com> >>>>> Sent: vrijdag 5 juli 2024 2:16 >>>>> To: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >>>>> dev@cxf.apache.org >>>>> 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 <drr...@gmail.com> >>>>>> Sent: donderdag 4 juli 2024 4:44 >>>>>> To: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >>>>>> dev@cxf.apache.org >>>>>> 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 <drr...@gmail.com> >>>>>>> Sent: woensdag 3 juli 2024 5:57 >>>>>>> To: Jean Pierre URKENS <jean-pierre.urk...@devoteam.com>; >>>>>>> dev@cxf.apache.org >>>>>>> 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.