Repository: quarkusio/quarkus
Issue: NullPointerException when no file is pass to to rest client reactive

### Describe the bug

When using rest client reactive with multipart formdata, if I don't pass any file then a NPE will occur
```
java.lang.NullPointerException: Cannot invoke "java.io.File.toPath()" because "<parameter1>.files" is null
```

the request object model look like this
```java
public class AppointmentFormData {

	@RestForm
	@PartType(MediaType.TEXT_PLAIN)
	public String documents;

	@RestForm("files")
	@PartType(MediaType.APPLICATION_OCTET_STREAM)
	public File files;

}

```

end the rest client 
```java
@Path("/appointments")
@RegisterRestClient(configKey = "appointment-service")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface V1AppointmentServiceRestClient {
	@GET
	@Path("/{id}")
	Uni<V1Appointment> getAppointmentById(@PathParam("id") Long id);

	@POST
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	Uni<V1Appointment> createAppointment(@MultipartForm AppointmentFormData data);
}
```



### Expected behavior

if there is no file, it should still send data successfully

### Actual behavior

throw NPE

### How to Reproduce?

_No response_

### Output of `uname -a` or `ver`

Darwin 20.6.0 Darwin Kernel Version 20.6.0: Wed Jun 23 00:26:31 PDT 2021; root:xnu-7195.141.2~5/RELEASE_X86_64 x86_64

### Output of `java -version`

openjdk version "16.0.1" 2021-04-20 OpenJDK Runtime Environment AdoptOpenJDK-16.0.1+9 (build 16.0.1+9) OpenJDK 64-Bit Server VM AdoptOpenJDK-16.0.1+9 (build 16.0.1+9, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.3.0.final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)

### Additional information

_No response_

Changed files:
integration-tests/rest-client-reactive-multipart/src/main/java/io/quarkus/it/rest/client/multipart/MultipartResource.java
extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java
integration-tests/rest-client-reactive-multipart/src/test/java/io/quarkus/it/rest/client/multipart/MultipartResourceTest.java

diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
index ee0b30d5d97..0e3eb72f9a8 100644
--- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
+++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java
@@ -1083,9 +1083,10 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand
                                     "No @PartType annotation found on multipart form field of type File: " +
                                             formClass.name() + "." + field.name());
                         }
-                        ResultHandle filePath = methodCreator.invokeVirtualMethod(
+                        BytecodeCreator ifFileNotNull = methodCreator.ifNotNull(fieldValue).trueBranch();
+                        ResultHandle filePath = ifFileNotNull.invokeVirtualMethod(
                                 MethodDescriptor.ofMethod(File.class, "toPath", Path.class), fieldValue);
-                        addFile(methodCreator, multipartForm, formParamName, partType, filePath);
+                        addFile(ifFileNotNull, multipartForm, formParamName, partType, filePath);
                     } else if (is(PATH, fieldClass, index)) {
                         // and so is path
                         if (partType == null) {
@@ -1093,10 +1094,12 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand
                                     "No @PartType annotation found on multipart form field of type Path: " +
                                             formClass.name() + "." + field.name());
                         }
-                        addFile(methodCreator, multipartForm, formParamName, partType, fieldValue);
+                        BytecodeCreator ifPathNotNull = methodCreator.ifNotNull(fieldValue).trueBranch();
+                        addFile(ifPathNotNull, multipartForm, formParamName, partType, fieldValue);
                     } else if (is(BUFFER, fieldClass, index)) {
                         // and buffer
-                        addBuffer(methodCreator, multipartForm, formParamName, partType, fieldValue, field);
+                        BytecodeCreator ifBufferNotNull = methodCreator.ifNotNull(fieldValue).trueBranch();
+                        addBuffer(ifBufferNotNull, multipartForm, formParamName, partType, fieldValue, field);
                     } else { // assume POJO:
                         addPojo(methodCreator, multipartForm, formParamName, partType, fieldValue, field);
                     }
@@ -1109,10 +1112,11 @@ private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHand
                         throw new IllegalArgumentException("Array of unsupported type: " + componentType.name()
                                 + " on " + formClassType.name() + "." + field.name());
                     }
-                    ResultHandle buffer = methodCreator.invokeStaticInterfaceMethod(
+                    BytecodeCreator ifArrayNotNull = methodCreator.ifNotNull(fieldValue).trueBranch();
+                    ResultHandle buffer = ifArrayNotNull.invokeStaticInterfaceMethod(
                             MethodDescriptor.ofMethod(Buffer.class, "buffer", Buffer.class, byte[].class),
                             fieldValue);
-                    addBuffer(methodCreator, multipartForm, formParamName, partType, buffer, field);
+                    addBuffer(ifArrayNotNull, multipartForm, formParamName, partType, buffer, field);
                     break;
                 case PRIMITIVE:
                     // primitives are converted to text and sent as attribute
@@ -1145,7 +1149,7 @@ private void addPojo(MethodCreator methodCreator, AssignableResultHandle multipa
      * add file upload, see {@link QuarkusMultipartForm#binaryFileUpload(String, String, String, String)} and
      * {@link QuarkusMultipartForm#textFileUpload(String, String, String, String)}
      */
-    private void addFile(MethodCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
+    private void addFile(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
             String partType, ResultHandle filePath) {
         ResultHandle fileNamePath = methodCreator.invokeInterfaceMethod(PATH_GET_FILENAME, filePath);
         ResultHandle fileName = methodCreator.invokeVirtualMethod(OBJECT_TO_STRING, fileNamePath);
@@ -1209,7 +1213,7 @@ private void addString(MethodCreator methodCreator, AssignableResultHandle multi
                         multipartForm, methodCreator.load(formParamName), fieldValue));
     }
 
-    private void addBuffer(MethodCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
+    private void addBuffer(BytecodeCreator methodCreator, AssignableResultHandle multipartForm, String formParamName,
             String partType, ResultHandle buffer, FieldInfo field) {
         if (partType == null) {
             throw new IllegalArgumentException(
diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java
index b3fb164f974..a43318d1920 100644
--- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java
+++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilder.java
@@ -300,7 +300,6 @@ private void configureBaseUrl(RestClientBuilder builder) {
                 clientConfigByConfigKey().uri);
 
         if (propertyOptional.isEmpty()) {
-            // mstodo is the url there string?
             propertyOptional = oneOf(clientConfigByClassName().url,
                     clientConfigByConfigKey().url);
         }
diff --git a/integration-tests/rest-client-reactive-multipart/src/main/java/io/quarkus/it/rest/client/multipart/MultipartResource.java b/integration-tests/rest-client-reactive-multipart/src/main/java/io/quarkus/it/rest/client/multipart/MultipartResource.java
index 4a040d7b963..7f9a5fc2923 100644
--- a/integration-tests/rest-client-reactive-multipart/src/main/java/io/quarkus/it/rest/client/multipart/MultipartResource.java
+++ b/integration-tests/rest-client-reactive-multipart/src/main/java/io/quarkus/it/rest/client/multipart/MultipartResource.java
@@ -14,6 +14,7 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 
@@ -75,9 +76,11 @@ public String sendByteArrayWithPojo(@RestQuery @DefaultValue("true") Boolean wit
     @Consumes(MediaType.TEXT_PLAIN)
     @Produces(MediaType.TEXT_PLAIN)
     @Blocking
-    public String sendByteArray() {
+    public String sendByteArray(@QueryParam("nullFile") @DefaultValue("false") boolean nullFile) {
         WithByteArrayAsBinaryFile data = new WithByteArrayAsBinaryFile();
-        data.file = HELLO_WORLD.getBytes(UTF_8);
+        if (!nullFile) {
+            data.file = HELLO_WORLD.getBytes(UTF_8);
+        }
         data.fileName = GREETING_TXT;
         return client.sendByteArrayAsBinaryFile(data);
     }
@@ -87,9 +90,11 @@ public String sendByteArray() {
     @Consumes(MediaType.TEXT_PLAIN)
     @Produces(MediaType.TEXT_PLAIN)
     @Blocking
-    public String sendBuffer() {
+    public String sendBuffer(@QueryParam("nullFile") @DefaultValue("false") boolean nullFile) {
         WithBufferAsBinaryFile data = new WithBufferAsBinaryFile();
-        data.file = Buffer.buffer(HELLO_WORLD);
+        if (!nullFile) {
+            data.file = Buffer.buffer(HELLO_WORLD);
+        }
         data.fileName = GREETING_TXT;
         return client.sendBufferAsBinaryFile(data);
     }
@@ -99,16 +104,19 @@ public String sendBuffer() {
     @Consumes(MediaType.TEXT_PLAIN)
     @Produces(MediaType.TEXT_PLAIN)
     @Blocking
-    public String sendFileAsBinary() throws IOException {
-        File tempFile = File.createTempFile("quarkus-test", ".bin");
-        tempFile.deleteOnExit();
+    public String sendFileAsBinary(@QueryParam("nullFile") @DefaultValue("false") boolean nullFile) throws IOException {
+        WithFileAsBinaryFile data = new WithFileAsBinaryFile();
 
-        try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
-            fileOutputStream.write(HELLO_WORLD.getBytes());
-        }
+        if (!nullFile) {
+            File tempFile = File.createTempFile("quarkus-test", ".bin");
+            tempFile.deleteOnExit();
 
-        WithFileAsBinaryFile data = new WithFileAsBinaryFile();
-        data.file = tempFile;
+            try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+                fileOutputStream.write(HELLO_WORLD.getBytes());
+            }
+
+            data.file = tempFile;
+        }
         data.fileName = GREETING_TXT;
         return client.sendFileAsBinaryFile(data);
     }
@@ -118,16 +126,19 @@ public String sendFileAsBinary() throws IOException {
     @Consumes(MediaType.TEXT_PLAIN)
     @Produces(MediaType.TEXT_PLAIN)
     @Blocking
-    public String sendPathAsBinary() throws IOException {
-        File tempFile = File.createTempFile("quarkus-test", ".bin");
-        tempFile.deleteOnExit();
+    public String sendPathAsBinary(@QueryParam("nullFile") @DefaultValue("false") boolean nullFile) throws IOException {
+        WithPathAsBinaryFile data = new WithPathAsBinaryFile();
 
-        try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
-            fileOutputStream.write(HELLO_WORLD.getBytes());
-        }
+        if (!nullFile) {
+            File tempFile = File.createTempFile("quarkus-test", ".bin");
+            tempFile.deleteOnExit();
 
-        WithPathAsBinaryFile data = new WithPathAsBinaryFile();
-        data.file = tempFile.toPath();
+            try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
+                fileOutputStream.write(HELLO_WORLD.getBytes());
+            }
+
+            data.file = tempFile.toPath();
+        }
         data.fileName = GREETING_TXT;
         return client.sendPathAsBinaryFile(data);
     }
@@ -198,7 +209,8 @@ public String sendPathAsText() throws IOException {
     @Path("/echo/binary")
     @Consumes(MediaType.MULTIPART_FORM_DATA)
     public String consumeMultipart(@MultipartForm MultipartBodyWithBinaryFile body) {
-        return String.format("fileOk:%s,nameOk:%s", containsHelloWorld(body.file), GREETING_TXT.equals(body.fileName));
+        return String.format("fileOk:%s,nameOk:%s", body.file == null ? "null" : containsHelloWorld(body.file),
+                GREETING_TXT.equals(body.fileName));
     }
 
     @POST
diff --git a/integration-tests/rest-client-reactive-multipart/src/test/java/io/quarkus/it/rest/client/multipart/MultipartResourceTest.java b/integration-tests/rest-client-reactive-multipart/src/test/java/io/quarkus/it/rest/client/multipart/MultipartResourceTest.java
index 129166a254b..40dc80cd531 100644
--- a/integration-tests/rest-client-reactive-multipart/src/test/java/io/quarkus/it/rest/client/multipart/MultipartResourceTest.java
+++ b/integration-tests/rest-client-reactive-multipart/src/test/java/io/quarkus/it/rest/client/multipart/MultipartResourceTest.java
@@ -22,6 +22,19 @@ public void shouldSendByteArrayAsBinaryFile() {
         // @formatter:on
     }
 
+    @Test
+    public void shouldSendNullByteArrayAsBinaryFile() {
+        // @formatter:off
+        given()
+                .queryParam("nullFile", "true")
+                .header("Content-Type", "text/plain")
+        .when().get("/client/byte-array-as-binary-file")
+        .then()
+                .statusCode(200)
+                .body(equalTo("fileOk:null,nameOk:true"));
+        // @formatter:on
+    }
+
     @Test
     public void shouldSendBufferAsBinaryFile() {
         // @formatter:off
@@ -34,6 +47,19 @@ public void shouldSendBufferAsBinaryFile() {
         // @formatter:on
     }
 
+    @Test
+    public void shouldSendNullBufferAsBinaryFile() {
+        // @formatter:off
+        given()
+                .queryParam("nullFile", "true")
+                .header("Content-Type", "text/plain")
+        .when().get("/client/buffer-as-binary-file")
+        .then()
+                .statusCode(200)
+                .body(equalTo("fileOk:null,nameOk:true"));
+        // @formatter:on
+    }
+
     @Test
     public void shouldSendFileAsBinaryFile() {
         // @formatter:off
@@ -46,6 +72,19 @@ public void shouldSendFileAsBinaryFile() {
         // @formatter:on
     }
 
+    @Test
+    public void shouldSendNullFileAsBinaryFile() {
+        // @formatter:off
+        given()
+                .queryParam("nullFile", "true")
+                .header("Content-Type", "text/plain")
+        .when().get("/client/file-as-binary-file")
+        .then()
+                .statusCode(200)
+                .body(equalTo("fileOk:null,nameOk:true"));
+        // @formatter:on
+    }
+
     @Test
     public void shouldSendPathAsBinaryFile() {
         // @formatter:off
@@ -58,6 +97,19 @@ public void shouldSendPathAsBinaryFile() {
         // @formatter:on
     }
 
+    @Test
+    public void shouldSendNullPathAsBinaryFile() {
+        // @formatter:off
+        given()
+                .queryParam("nullFile", "true")
+                .header("Content-Type", "text/plain")
+        .when().get("/client/path-as-binary-file")
+        .then()
+                .statusCode(200)
+                .body(equalTo("fileOk:null,nameOk:true"));
+        // @formatter:on
+    }
+
     @Test
     public void shouldSendByteArrayAsTextFile() {
         // @formatter:off