Repository: quarkusio/quarkus
Issue: RESTEasy Reactive: null fields in multipart responses cause NullPointerException

### Describe the bug

When trying to return a multipart response with null fields (see the code below), MultipartMessageBodyWriter throws a NullPointerException

### Expected behavior

_No response_

### Actual behavior

```java
2022-01-12 23:13:34,634 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-0) HTTP Request to /give-me-file/empty failed, error id: d54b7825-d1ce-4881-a049-01b23da53ee4-1: java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "entity" is null
        at org.jboss.resteasy.reactive.server.core.multipart.MultipartMessageBodyWriter.serialiseEntity(MultipartMessageBodyWriter.java:120)
        at org.jboss.resteasy.reactive.server.core.multipart.MultipartMessageBodyWriter.write(MultipartMessageBodyWriter.java:83)
        at org.jboss.resteasy.reactive.server.core.multipart.MultipartMessageBodyWriter.writeMultiformData(MultipartMessageBodyWriter.java:57)
        at org.jboss.resteasy.reactive.server.core.multipart.MultipartMessageBodyWriter.writeResponse(MultipartMessageBodyWriter.java:44)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers.invokeWriter(ServerSerialisers.java:207)
        at org.jboss.resteasy.reactive.server.core.ServerSerialisers.invokeWriter(ServerSerialisers.java:178)
        at org.jboss.resteasy.reactive.server.core.serialization.FixedEntityWriter.write(FixedEntityWriter.java:26)
        at org.jboss.resteasy.reactive.server.handlers.ResponseWriterHandler.handle(ResponseWriterHandler.java:32)
        at org.jboss.resteasy.reactive.server.handlers.ResponseWriterHandler.handle(ResponseWriterHandler.java:15)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)
```


### How to Reproduce?

```java
    @Path("/give-me-file")
    public static class Resource {

        @GET
        @Produces(MediaType.MULTIPART_FORM_DATA)
        @Path("/empty")
        public MultipartData getEmptyData() {
            return new MultipartData(null, null, null);
        }
    }

    public static class MultipartData {
        @RestForm
        @PartType(MediaType.TEXT_PLAIN)
        public String name;

        @RestForm
        @PartType(MediaType.APPLICATION_OCTET_STREAM)
        public File file;

        @RestForm
        @PartType(MediaType.APPLICATION_JSON)
        public Panda panda;

        public MultipartData() {
        }

        public MultipartData(String name, File file, Panda panda) {
            this.name = name;
            this.file = file;
            this.panda = panda;
        }
    }

    public static class Panda {
        public String weight;
        public String height;
        public String mood;

        public Panda() {
        }

        public Panda(String weight, String height, String mood) {
            this.weight = weight;
            this.height = height;
            this.mood = mood;
        }
    }
```

### Output of `uname -a` or `ver`

Linux ryzen 5.15.12-200.fc35.x86_64 #1 SMP Wed Dec 29 15:03:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

### Output of `java -version`

openjdk version "17.0.1" 2021-10-19 OpenJDK Runtime Environment Temurin-17.0.1+12 (build 17.0.1+12) OpenJDK 64-Bit Server VM Temurin-17.0.1+12 (build 17.0.1+12, mixed mode, sharing)

### GraalVM version (if different from Java)

n/a

### Quarkus version or git rev

main

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739) Maven home: /home/michal/.sdkman/candidates/maven/current Java version: 17.0.1, vendor: Eclipse Adoptium, runtime: /home/michal/.sdkman/candidates/java/17.0.1-tem Default locale: pl_PL, platform encoding: UTF-8 OS name: "linux", version: "5.15.12-200.fc35.x86_64", arch: "amd64", family: "unix"

### Additional information

_No response_

Changed files:
extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java
independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java
extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputResource.java

diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputResource.java
index 8f5cfcb2006..9288930b311 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputResource.java
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputResource.java
@@ -49,4 +49,14 @@ public MultipartOutputFileResponse complex() {
         return response;
     }
 
+    @GET
+    @Path("/with-null-fields")
+    @Produces(MediaType.MULTIPART_FORM_DATA)
+    public MultipartOutputFileResponse nullFields() {
+        MultipartOutputFileResponse response = new MultipartOutputFileResponse();
+        response.name = null;
+        response.file = null;
+        return response;
+    }
+
 }
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java
index b1333f243d2..b33a1cd1a7c 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java
@@ -67,6 +67,17 @@ public void testWithFiles() {
         assertContainsFile(response, "file", MediaType.APPLICATION_OCTET_STREAM, "lorem.txt");
     }
 
+    @Test
+    public void testWithNullFields() {
+        RestAssured
+                .given()
+                .get("/multipart/output/with-null-fields")
+                .then()
+                .contentType(ContentType.MULTIPART)
+                .log().all()
+                .statusCode(200); // should return 200 with no parts
+    }
+
     private void assertContainsFile(String response, String name, String contentType, String fileName) {
         String[] lines = response.split("--");
         assertThat(lines).anyMatch(line -> line.contains(String.format(EXPECTED_CONTENT_DISPOSITION_FILE_PART, name, fileName))
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java
index efcfcc83eed..b836994dc4d 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java
@@ -69,20 +69,22 @@ private void write(List<PartItem> parts, String boundary, OutputStream outputStr
         Charset charset = requestContext.getDeployment().getRuntimeConfiguration().body().defaultCharset();
         String boundaryLine = "--" + boundary;
         for (PartItem part : parts) {
-            // write boundary: --...
-            writeLine(outputStream, boundaryLine, charset);
-            // write content disposition header
-            writeLine(outputStream, HttpHeaders.CONTENT_DISPOSITION + ": form-data; name=\\"" + part.getName() + "\\""
-                    + getFileNameIfFile(part.getValue()), charset);
-            // write content content type
-            writeLine(outputStream, HttpHeaders.CONTENT_TYPE + ": " + part.getType(), charset);
-            // extra line
-            writeLine(outputStream, charset);
-
-            // write content
-            write(outputStream, serialiseEntity(part.getValue(), part.getType(), requestContext));
-            // extra line
-            writeLine(outputStream, charset);
+            if (part.getValue() != null) {
+                // write boundary: --...
+                writeLine(outputStream, boundaryLine, charset);
+                // write content disposition header
+                writeLine(outputStream, HttpHeaders.CONTENT_DISPOSITION + ": form-data; name=\\"" + part.getName() + "\\""
+                        + getFileNameIfFile(part.getValue()), charset);
+                // write content content type
+                writeLine(outputStream, HttpHeaders.CONTENT_TYPE + ": " + part.getType(), charset);
+                // extra line
+                writeLine(outputStream, charset);
+
+                // write content
+                write(outputStream, serialiseEntity(part.getValue(), part.getType(), requestContext));
+                // extra line
+                writeLine(outputStream, charset);
+            }
         }
 
         // write boundary: -- ... --