Skip to content

Commit

Permalink
Merge pull request #31312 from quarkusio/#31213
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
geoand authored Feb 21, 2023
2 parents a0a60d4 + 9d435b7 commit e0afb63
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public class ResteasyReactiveCDIProcessor {
AutoInjectAnnotationBuildItem contextInjection(
BuildProducer<AdditionalBeanBuildItem> additionalBeanBuildItemBuildProducer) {
additionalBeanBuildItemBuildProducer
.produce(AdditionalBeanBuildItem.builder().addBeanClasses(ContextProducers.class, QuarkusContextProducers.class)
.produce(AdditionalBeanBuildItem.builder()
.addBeanClasses(ContextProducers.class, QuarkusContextProducers.class)
.build());
return new AutoInjectAnnotationBuildItem(ResteasyReactiveServerDotNames.CONTEXT,
DotName.createSimple(BeanParam.class.getName()));
Expand Down
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);

}
}
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);
}
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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Disposes;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Singleton;
import jakarta.ws.rs.ext.Providers;
Expand Down Expand Up @@ -30,4 +31,15 @@ HttpServerResponse httpServerResponse() {
Providers providers() {
return new ProvidersImpl(ResteasyReactiveRecorder.getCurrentDeployment());
}

@RequestScoped
@Produces
CloserImpl closer() {
return new CloserImpl();
}

void closeCloser(@Disposes CloserImpl closer) {
closer.close();
}

}

0 comments on commit e0afb63

Please sign in to comment.