-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Path as a JAX-RS method body type
(cherry picked from commit 9613528)
- Loading branch information
Showing
6 changed files
with
282 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
...test/java/io/quarkus/resteasy/reactive/server/test/multipart/PathInputWithDeleteTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package io.quarkus.resteasy.reactive.server.test.multipart; | ||
|
||
import static org.hamcrest.CoreMatchers.equalTo; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.util.function.Supplier; | ||
|
||
import jakarta.ws.rs.Consumes; | ||
import jakarta.ws.rs.POST; | ||
import jakarta.ws.rs.Path; | ||
|
||
import org.jboss.shrinkwrap.api.ShrinkWrap; | ||
import org.jboss.shrinkwrap.api.asset.StringAsset; | ||
import org.jboss.shrinkwrap.api.spec.JavaArchive; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
|
||
public class PathInputWithDeleteTest extends AbstractMultipartTest { | ||
|
||
private static final java.nio.file.Path uploadDir = Paths.get("file-uploads"); | ||
|
||
@RegisterExtension | ||
static QuarkusUnitTest test = new QuarkusUnitTest() | ||
.setArchiveProducer(new Supplier<>() { | ||
@Override | ||
public JavaArchive get() { | ||
return ShrinkWrap.create(JavaArchive.class) | ||
.addClasses(Resource.class) | ||
.addAsResource(new StringAsset( | ||
"quarkus.http.body.uploads-directory=" | ||
+ uploadDir.toString() + "\n"), | ||
"application.properties"); | ||
} | ||
|
||
}); | ||
|
||
private final File HTML_FILE = new File("./src/test/resources/test.html"); | ||
private final File HTML_FILE2 = new File("./src/test/resources/test2.html"); | ||
|
||
@Test | ||
public void test() throws IOException { | ||
RestAssured.given() | ||
.contentType("application/octet-stream") | ||
.body(HTML_FILE) | ||
.when() | ||
.post("/test") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo(fileSizeAsStr(HTML_FILE))); | ||
|
||
awaitUploadDirectoryToEmpty(uploadDir); | ||
|
||
RestAssured.given() | ||
.contentType("application/octet-stream") | ||
.body(HTML_FILE2) | ||
.when() | ||
.post("/test") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo(fileSizeAsStr(HTML_FILE2))); | ||
|
||
awaitUploadDirectoryToEmpty(uploadDir); | ||
} | ||
|
||
@Path("test") | ||
public static class Resource { | ||
|
||
@POST | ||
@Consumes("application/octet-stream") | ||
public long size(java.nio.file.Path file) throws IOException { | ||
return Files.size(file); | ||
} | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
...t/java/io/quarkus/resteasy/reactive/server/test/multipart/PathInputWithoutDeleteTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package io.quarkus.resteasy.reactive.server.test.multipart; | ||
|
||
import static org.hamcrest.CoreMatchers.equalTo; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.util.function.Supplier; | ||
|
||
import jakarta.ws.rs.Consumes; | ||
import jakarta.ws.rs.POST; | ||
import jakarta.ws.rs.Path; | ||
|
||
import org.jboss.shrinkwrap.api.ShrinkWrap; | ||
import org.jboss.shrinkwrap.api.asset.StringAsset; | ||
import org.jboss.shrinkwrap.api.spec.JavaArchive; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
|
||
public class PathInputWithoutDeleteTest extends AbstractMultipartTest { | ||
|
||
private static final java.nio.file.Path uploadDir = Paths.get("file-uploads"); | ||
|
||
@RegisterExtension | ||
static QuarkusUnitTest test = new QuarkusUnitTest() | ||
.setArchiveProducer(new Supplier<>() { | ||
@Override | ||
public JavaArchive get() { | ||
return ShrinkWrap.create(JavaArchive.class) | ||
.addClasses(Resource.class) | ||
.addAsResource(new StringAsset( | ||
// keep the files around so we can assert the outcome | ||
"quarkus.http.body.delete-uploaded-files-on-end=false\nquarkus.http.body.uploads-directory=" | ||
+ uploadDir.toString() + "\n"), | ||
"application.properties"); | ||
} | ||
|
||
}); | ||
|
||
private final File HTML_FILE = new File("./src/test/resources/test.html"); | ||
private final File HTML_FILE2 = new File("./src/test/resources/test2.html"); | ||
|
||
@BeforeEach | ||
public void assertEmptyUploads() { | ||
Assertions.assertTrue(isDirectoryEmpty(uploadDir)); | ||
} | ||
|
||
@AfterEach | ||
public void clearDirectory() { | ||
clearDirectory(uploadDir); | ||
} | ||
|
||
@Test | ||
public void test() throws IOException { | ||
RestAssured.given() | ||
.contentType("application/octet-stream") | ||
.body(HTML_FILE) | ||
.when() | ||
.post("/test") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo(fileSizeAsStr(HTML_FILE))); | ||
|
||
// ensure that the 3 uploaded files where created on disk | ||
Assertions.assertEquals(1, uploadDir.toFile().listFiles().length); | ||
|
||
RestAssured.given() | ||
.contentType("application/octet-stream") | ||
.body(HTML_FILE2) | ||
.when() | ||
.post("/test") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo(fileSizeAsStr(HTML_FILE2))); | ||
|
||
// ensure that the 3 uploaded files where created on disk | ||
Assertions.assertEquals(2, uploadDir.toFile().listFiles().length); | ||
} | ||
|
||
@Path("test") | ||
public static class Resource { | ||
|
||
@POST | ||
@Consumes("application/octet-stream") | ||
public long size(java.nio.file.Path file) throws IOException { | ||
return Files.size(file); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
...c/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusServerPathBodyHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package io.quarkus.resteasy.reactive.server.runtime; | ||
|
||
import static org.jboss.resteasy.reactive.common.providers.serialisers.FileBodyHandler.PREFIX; | ||
import static org.jboss.resteasy.reactive.common.providers.serialisers.FileBodyHandler.SUFFIX; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.UncheckedIOException; | ||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Type; | ||
import java.nio.file.Files; | ||
import java.nio.file.NoSuchFileException; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
|
||
import jakarta.ws.rs.WebApplicationException; | ||
import jakarta.ws.rs.container.CompletionCallback; | ||
import jakarta.ws.rs.core.MediaType; | ||
import jakarta.ws.rs.core.MultivaluedMap; | ||
|
||
import org.jboss.logging.Logger; | ||
import org.jboss.resteasy.reactive.common.providers.serialisers.FileBodyHandler; | ||
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; | ||
import org.jboss.resteasy.reactive.server.spi.RuntimeConfiguration; | ||
import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; | ||
import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; | ||
|
||
public class QuarkusServerPathBodyHandler implements ServerMessageBodyReader<Path> { | ||
|
||
private static final Logger log = Logger.getLogger(QuarkusServerPathBodyHandler.class); | ||
|
||
@Override | ||
public boolean isReadable(Class<?> type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, | ||
MediaType mediaType) { | ||
return Path.class.equals(type); | ||
} | ||
|
||
@Override | ||
public Path readFrom(Class<Path> type, Type genericType, MediaType mediaType, ServerRequestContext context) | ||
throws WebApplicationException, IOException { | ||
Path file = createFile(context); | ||
return FileBodyHandler.doRead(context.getRequestHeaders().getRequestHeaders(), context.getInputStream(), file.toFile()) | ||
.toPath(); | ||
} | ||
|
||
@Override | ||
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { | ||
return File.class.equals(type); | ||
} | ||
|
||
@Override | ||
public Path readFrom(Class<Path> type, Type genericType, Annotation[] annotations, MediaType mediaType, | ||
MultivaluedMap<String, String> httpHeaders, InputStream entityStream) | ||
throws IOException, WebApplicationException { | ||
// unfortunately we don't do much here to avoid the file leak | ||
// however this should never be called in a real world scenario | ||
return FileBodyHandler.doRead(httpHeaders, entityStream, Files.createTempFile(PREFIX, SUFFIX).toFile()).toPath(); | ||
} | ||
|
||
static Path createFile(ServerRequestContext context) throws IOException { | ||
RuntimeConfiguration.Body runtimeBodyConfiguration = ResteasyReactiveRecorder.getCurrentDeployment() | ||
.getRuntimeConfiguration().body(); | ||
boolean deleteUploadedFilesOnEnd = runtimeBodyConfiguration.deleteUploadedFilesOnEnd(); | ||
String uploadsDirectoryStr = runtimeBodyConfiguration.uploadsDirectory(); | ||
Path uploadDirectory = Paths.get(uploadsDirectoryStr); | ||
try { | ||
Files.createDirectories(uploadDirectory); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
|
||
Path file = Files.createTempFile(uploadDirectory, PREFIX, SUFFIX); | ||
if (deleteUploadedFilesOnEnd) { | ||
context.registerCompletionCallback(new CompletionCallback() { | ||
@Override | ||
public void onComplete(Throwable throwable) { | ||
ResteasyReactiveRecorder.EXECUTOR_SUPPLIER.get().execute(new Runnable() { | ||
@Override | ||
public void run() { | ||
if (Files.exists(file)) { | ||
try { | ||
Files.delete(file); | ||
} catch (NoSuchFileException e) { // ignore | ||
} catch (IOException e) { | ||
log.error("Cannot remove uploaded file " + file, e); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
return file; | ||
} | ||
} |