PathProvider supports Path entities, in preparation for future default support in JAX-RS 4.x / Jersey 4.x.

Signed-off-by: Markus KARG <markus@headcrashing.eu>
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/PathProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/PathProvider.java
new file mode 100644
index 0000000..461a4f8
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/PathProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Markus KARG and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.message.internal;
+
+import static jakarta.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static jakarta.ws.rs.core.MediaType.WILDCARD;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+
+import jakarta.inject.Singleton;
+
+/**
+ * Provider for marshalling/un-marshalling of {@code application/octet-stream}
+ * entity type to/from a {@link Path} instance.
+ *
+ * @author Markus KARG
+ */
+@Produces({APPLICATION_OCTET_STREAM, WILDCARD})
+@Consumes({APPLICATION_OCTET_STREAM, WILDCARD})
+@Singleton
+public final class PathProvider extends AbstractMessageReaderWriterProvider<Path> {
+
+    @Override
+    public final boolean isReadable(final Class<?> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType) {
+        return Path.class == type;
+    }
+
+    @Override
+    public final Path readFrom(final Class<Path> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType,
+            final MultivaluedMap<String, String> httpHeaders,
+            final InputStream entityStream) throws IOException {
+        final var path = Utils.createTempFile().toPath();
+        Files.copy(entityStream, path, StandardCopyOption.REPLACE_EXISTING);
+        return path;
+    }
+
+    @Override
+    public final boolean isWriteable(final Class<?> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType) {
+        return Path.class.isAssignableFrom(type);
+    }
+
+    @Override
+    public final void writeTo(final Path t,
+            final Class<?> type,
+            final Type genericType,
+            final Annotation[] annotations,
+            final MediaType mediaType,
+            final MultivaluedMap<String, Object> httpHeaders,
+            final OutputStream entityStream) throws IOException {
+        Files.copy(t, entityStream);
+    }
+}
diff --git a/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json b/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json
index ef2ae4d..184402f 100644
--- a/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json
+++ b/core-common/src/main/resources/META-INF/native-image/org.glassfish.jersey.core/jersey-common/reflect-config.json
@@ -58,6 +58,12 @@
     "allDeclaredConstructors":true
   },
   {
+    "name":"org.glassfish.jersey.message.internal.PathProvider",
+    "allDeclaredFields":true,
+    "allDeclaredMethods":true,
+    "allDeclaredConstructors":true
+  },
+  {
     "name":"org.glassfish.jersey.message.internal.FormMultivaluedMapProvider",
     "allDeclaredFields":true,
     "allDeclaredMethods":true,
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
index 77a74a6..2a8f199 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -72,6 +72,7 @@
 import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
 import org.glassfish.jersey.jettison.JettisonFeature;
 import org.glassfish.jersey.message.internal.FileProvider;
+import org.glassfish.jersey.message.internal.PathProvider;
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.codehaus.jettison.json.JSONArray;
@@ -325,13 +326,13 @@
 
     @Override
     protected Application configure() {
-        return ((ResourceConfig) super.configure()).register(new JettisonFeature());
+        return ((ResourceConfig) super.configure()).register(new JettisonFeature()).register(new PathProvider());
     }
 
     @Override
     protected void configureClient(final ClientConfig config) {
         super.configureClient(config);
-        config.register(new JettisonFeature());
+        config.register(new JettisonFeature()).register(new PathProvider());
     }
 
     @Test
@@ -431,6 +432,20 @@
         _test(in, FileResource.class);
     }
 
+    @Path("PathResource")
+    public static class PathResource extends AResource<java.nio.file.Path> {
+    }
+
+    @Test
+    @Execution(ExecutionMode.CONCURRENT)
+    public void testPathRepresentation() throws IOException {
+        final var pp = new PathProvider();
+        final var in = pp.readFrom(java.nio.file.Path.class, java.nio.file.Path.class, null, null, null,
+                new ByteArrayInputStream("CONTENT".getBytes()));
+
+        _test(in, PathResource.class);
+    }
+
     @Produces("application/x-www-form-urlencoded")
     @Consumes("application/x-www-form-urlencoded")
     @Path("FormResource")