-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Add a way for RESTEasy Reactive to close arbitrary Closeable services
- Loading branch information
Showing
5 changed files
with
274 additions
and
1 deletion.
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
212 changes: 212 additions & 0 deletions
212
...eactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CloserTest.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,212 @@ | ||
package io.quarkus.resteasy.reactive.server.test; | ||
|
||
import static io.restassured.RestAssured.get; | ||
import static org.hamcrest.Matchers.equalTo; | ||
|
||
import java.io.Closeable; | ||
import java.io.IOException; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
import java.util.function.Supplier; | ||
|
||
import jakarta.enterprise.context.RequestScoped; | ||
import jakarta.inject.Inject; | ||
import jakarta.inject.Singleton; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.core.Context; | ||
|
||
import org.jboss.shrinkwrap.api.ShrinkWrap; | ||
import org.jboss.shrinkwrap.api.spec.JavaArchive; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.resteasy.reactive.server.Closer; | ||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.smallrye.mutiny.Uni; | ||
|
||
public class CloserTest { | ||
|
||
@RegisterExtension | ||
static QuarkusUnitTest test = new QuarkusUnitTest() | ||
.setArchiveProducer(new Supplier<>() { | ||
@Override | ||
public JavaArchive get() { | ||
return ShrinkWrap.create(JavaArchive.class) | ||
.addClasses(PerRequestResource.class, SingletonResource.class, CounterResource.class, | ||
Counter.class); | ||
} | ||
}); | ||
|
||
@Test | ||
public void test() { | ||
get("/counter/singleton") | ||
.then() | ||
.body(equalTo("0")); | ||
get("/counter/uni-singleton") | ||
.then() | ||
.body(equalTo("0")); | ||
get("/counter/per-request") | ||
.then() | ||
.body(equalTo("0")); | ||
|
||
get("/singleton") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo("0")); | ||
get("/singleton") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo("1")); | ||
get("/counter/singleton") | ||
.then() | ||
.body(equalTo("2")); | ||
get("/counter/uni-singleton") | ||
.then() | ||
.body(equalTo("0")); | ||
get("/counter/per-request") | ||
.then() | ||
.body(equalTo("0")); | ||
|
||
get("/uni-singleton") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo("0")); | ||
get("/uni-singleton") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo("1")); | ||
get("/counter/singleton") | ||
.then() | ||
.body(equalTo("2")); | ||
get("/counter/uni-singleton") | ||
.then() | ||
.body(equalTo("2")); | ||
get("/counter/per-request") | ||
.then() | ||
.body(equalTo("0")); | ||
|
||
get("/per-request") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo("0")); | ||
get("/per-request") | ||
.then() | ||
.statusCode(200) | ||
.body(equalTo("1")); | ||
get("/counter/singleton") | ||
.then() | ||
.body(equalTo("2")); | ||
get("/counter/uni-singleton") | ||
.then() | ||
.body(equalTo("2")); | ||
get("/counter/per-request") | ||
.then() | ||
.body(equalTo("2")); | ||
} | ||
|
||
@Path("per-request") | ||
@RequestScoped | ||
public static class PerRequestResource implements Closeable { | ||
private final Closer closer; | ||
private final Counter counter; | ||
|
||
public PerRequestResource(Closer closer, Counter counter) { | ||
this.closer = closer; | ||
this.counter = counter; | ||
} | ||
|
||
@GET | ||
public int get() { | ||
closer.add(this); | ||
return counter.perRequest.get(); | ||
} | ||
|
||
public void close() throws IOException { | ||
counter.perRequest.incrementAndGet(); | ||
} | ||
} | ||
|
||
@Path("singleton") | ||
public static class SingletonResource implements Closeable { | ||
|
||
private final Counter counter; | ||
|
||
public SingletonResource(Counter counter) { | ||
this.counter = counter; | ||
} | ||
|
||
@GET | ||
public int get(@Context Closer closer) { | ||
closer.add(this); | ||
return counter.singleton.get(); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
counter.singleton.incrementAndGet(); | ||
} | ||
} | ||
|
||
@Path("uni-singleton") | ||
public static class UniSingletonResource implements Closeable { | ||
|
||
@Inject | ||
Counter counter; | ||
|
||
@Inject | ||
Closer closer; | ||
|
||
public UniSingletonResource(Counter counter) { | ||
this.counter = counter; | ||
} | ||
|
||
@GET | ||
public Uni<Integer> get() { | ||
return Uni.createFrom().completionStage(() -> CompletableFuture.completedStage(null)) | ||
.invoke(() -> closer.add(UniSingletonResource.this)) | ||
.map(v -> counter.uniSingleton.get()); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
counter.uniSingleton.incrementAndGet(); | ||
} | ||
} | ||
|
||
@Path("counter") | ||
public static class CounterResource { | ||
|
||
private final Counter counter; | ||
|
||
public CounterResource(Counter counter) { | ||
this.counter = counter; | ||
} | ||
|
||
@Path("singleton") | ||
@GET | ||
public int singletonCount() { | ||
return counter.singleton.get(); | ||
} | ||
|
||
@Path("uni-singleton") | ||
@GET | ||
public int uniSingleton() { | ||
return counter.uniSingleton.get(); | ||
} | ||
|
||
@Path("per-request") | ||
@GET | ||
public int perRequestCount() { | ||
return counter.perRequest.get(); | ||
} | ||
} | ||
|
||
@Singleton | ||
public static class Counter { | ||
public final AtomicInteger perRequest = new AtomicInteger(0); | ||
public final AtomicInteger singleton = new AtomicInteger(0); | ||
public final AtomicInteger uniSingleton = new AtomicInteger(0); | ||
|
||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...s-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/Closer.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,17 @@ | ||
package io.quarkus.resteasy.reactive.server; | ||
|
||
import java.io.Closeable; | ||
|
||
/** | ||
* A service that allows users to close any {@link Closeable} that | ||
* when the request completes. | ||
* <p> | ||
* Meant to be used a Resource Method parameter using {@link jakarta.ws.rs.core.Context} | ||
*/ | ||
public interface Closer { | ||
|
||
/** | ||
* Register a new {@link Closeable} that is to be closed when the request completes. | ||
*/ | ||
void add(Closeable c); | ||
} |
31 changes: 31 additions & 0 deletions
31
...eactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/CloserImpl.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,31 @@ | ||
package io.quarkus.resteasy.reactive.server.runtime; | ||
|
||
import java.io.Closeable; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.jboss.logging.Logger; | ||
|
||
import io.quarkus.resteasy.reactive.server.Closer; | ||
|
||
public class CloserImpl implements Closer { | ||
|
||
private static final Logger log = Logger.getLogger(CloserImpl.class); | ||
|
||
private final List<Closeable> closables = new ArrayList<>(); | ||
|
||
@Override | ||
public void add(Closeable c) { | ||
closables.add(c); | ||
} | ||
|
||
void close() { | ||
for (Closeable closable : closables) { | ||
try { | ||
closable.close(); | ||
} catch (Exception e) { | ||
log.warn("Unable to perform close operation", e); | ||
} | ||
} | ||
} | ||
} |
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