From 5f0986600127a8e092f032e7f3906c5c350a36bb Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 11 Sep 2023 21:36:31 -0300 Subject: [PATCH] Use IronJacamar's WorkManager and BootstrapContext implementation - This gets rid of the original implementation I had for `jakarta.resource.spi.BootstrapContext` and `jakarta.resource.spi.work.WorkManager` and uses the implementations provided by IronJacamar - Fixes #43 - Initial work on #42 --- deployment/pom.xml | 4 + .../deployment/IronJacamarProcessor.java | 9 ++ runtime/pom.xml | 4 + .../io/quarkiverse/ironjacamar/Defaults.java | 3 + .../runtime/IronJacamarRecorder.java | 49 +++++-- .../runtime/IronJacamarVerticle.java | 38 ++---- .../runtime/QuarkusBootstrapContext.java | 51 ------- .../runtime/QuarkusWorkManager.java | 127 ------------------ .../security/QuarkusCallbackHandler.java | 21 +++ .../security/QuarkusSecurityContext.java | 24 ++++ .../security/QuarkusSecurityIntegration.java | 37 +++++ 11 files changed, 157 insertions(+), 210 deletions(-) delete mode 100644 runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusBootstrapContext.java delete mode 100644 runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusWorkManager.java create mode 100644 runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusCallbackHandler.java create mode 100644 runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityContext.java create mode 100644 runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityIntegration.java diff --git a/deployment/pom.xml b/deployment/pom.xml index e2bc653..ae3df0a 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -21,6 +21,10 @@ io.quarkus quarkus-narayana-jta-deployment + + io.quarkus + quarkus-smallrye-context-propagation-deployment + io.quarkiverse.ironjacamar quarkus-ironjacamar diff --git a/deployment/src/main/java/io/quarkiverse/ironjacamar/deployment/IronJacamarProcessor.java b/deployment/src/main/java/io/quarkiverse/ironjacamar/deployment/IronJacamarProcessor.java index 17e393c..56a109c 100644 --- a/deployment/src/main/java/io/quarkiverse/ironjacamar/deployment/IronJacamarProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/ironjacamar/deployment/IronJacamarProcessor.java @@ -34,6 +34,7 @@ import io.quarkiverse.ironjacamar.runtime.QuarkusIronJacamarLogger; import io.quarkus.arc.BeanDestroyer; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.BeanContainerBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -229,6 +230,14 @@ void registerSyntheticBeans( } } + @BuildStep + @Record(value = ExecutionTime.STATIC_INIT) + void initDefaultBootstrapContext(BeanContainerBuildItem beanContainerBuildItem, + IronJacamarRecorder recorder) { + // Create the default bootstrap context + recorder.initDefaultBoostrapContext(); + } + @BuildStep @Record(value = ExecutionTime.RUNTIME_INIT) @Consume(SyntheticBeansRuntimeInitBuildItem.class) diff --git a/runtime/pom.xml b/runtime/pom.xml index 3422ea9..1b29e76 100644 --- a/runtime/pom.xml +++ b/runtime/pom.xml @@ -22,6 +22,10 @@ io.quarkus quarkus-narayana-jta + + io.quarkus + quarkus-smallrye-context-propagation + jakarta.resource jakarta.resource-api diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/Defaults.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/Defaults.java index 9f5b03a..44fbc63 100644 --- a/runtime/src/main/java/io/quarkiverse/ironjacamar/Defaults.java +++ b/runtime/src/main/java/io/quarkiverse/ironjacamar/Defaults.java @@ -3,4 +3,7 @@ public interface Defaults { String DEFAULT_RESOURCE_ADAPTER_NAME = ""; String DEFAULT_ACTIVATION_SPEC_NAME = ""; + String DEFAULT_WORK_MANAGER_NAME = "default"; + String DEFAULT_BOOTSTRAP_CONTEXT_NAME = "default"; + } diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarRecorder.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarRecorder.java index 35f0245..f054d88 100644 --- a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarRecorder.java +++ b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarRecorder.java @@ -1,18 +1,26 @@ package io.quarkiverse.ironjacamar.runtime; +import static io.quarkiverse.ironjacamar.Defaults.DEFAULT_BOOTSTRAP_CONTEXT_NAME; +import static io.quarkiverse.ironjacamar.Defaults.DEFAULT_WORK_MANAGER_NAME; + import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; import jakarta.enterprise.inject.spi.DeploymentException; import jakarta.resource.ResourceException; -import jakarta.resource.spi.XATerminator; -import jakarta.transaction.TransactionSynchronizationRegistry; +import org.eclipse.microprofile.context.ManagedExecutor; +import org.jboss.jca.core.api.bootstrap.CloneableBootstrapContext; import org.jboss.jca.core.api.connectionmanager.ccm.CachedConnectionManager; +import org.jboss.jca.core.bootstrapcontext.BaseCloneableBootstrapContext; +import org.jboss.jca.core.bootstrapcontext.BootstrapContextCoordinator; import org.jboss.jca.core.connectionmanager.ccm.CachedConnectionManagerImpl; import org.jboss.jca.core.spi.transaction.TransactionIntegration; +import org.jboss.jca.core.workmanager.WorkManagerCoordinator; +import org.jboss.jca.core.workmanager.WorkManagerImpl; +import io.quarkiverse.ironjacamar.runtime.security.QuarkusSecurityIntegration; import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.SyntheticCreationalContext; @@ -65,18 +73,43 @@ public CachedConnectionManager apply(SyntheticCreationalContext> initResourceAdapter( String key, Supplier vertxSupplier) { ArcContainer container = Arc.container(); Vertx vertx = vertxSupplier.get(); IronJacamarContainer ijContainer = container.select(IronJacamarContainer.class, Identifier.Literal.of(key)).get(); - // Lookup JTA beans - TransactionSynchronizationRegistry tsr = container.select(TransactionSynchronizationRegistry.class).get(); - XATerminator xaTerminator = container.select(XATerminator.class).get(); - IronJacamarVerticle verticle = new IronJacamarVerticle(ijContainer.getResourceAdapter(), tsr, xaTerminator, - key, - ijContainer.getResourceAdapterFactory().getDescription()); + CloneableBootstrapContext bootstrapContext = BootstrapContextCoordinator.getInstance().getDefaultBootstrapContext(); + IronJacamarVerticle verticle = new IronJacamarVerticle(key, ijContainer.getResourceAdapterFactory().getDescription(), + ijContainer.getResourceAdapter(), + bootstrapContext); Future future = vertx.deployVerticle(verticle, new DeploymentOptions() .setWorkerPoolName("jca-worker-pool-" + key) .setWorkerPoolSize(1) diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarVerticle.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarVerticle.java index f9a6bfa..cd08018 100644 --- a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarVerticle.java +++ b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/IronJacamarVerticle.java @@ -2,53 +2,43 @@ import java.util.Objects; -import jakarta.resource.spi.BootstrapContext; import jakarta.resource.spi.ResourceAdapter; -import jakarta.resource.spi.XATerminator; -import jakarta.transaction.TransactionSynchronizationRegistry; + +import org.jboss.jca.core.api.bootstrap.CloneableBootstrapContext; +import org.jboss.jca.core.api.workmanager.WorkManager; import io.vertx.core.AbstractVerticle; -import io.vertx.core.Promise; /** * A Vert.x {@link io.vertx.core.Verticle} that starts and stops a JCA {@link ResourceAdapter}. */ final class IronJacamarVerticle extends AbstractVerticle { - private final ResourceAdapter ra; - private final TransactionSynchronizationRegistry tsr; - private final XATerminator xaTerminator; private final String id; private final String description; + private final ResourceAdapter ra; + private final CloneableBootstrapContext bootstrapContext; - private QuarkusWorkManager workManager; - - public IronJacamarVerticle(ResourceAdapter resourceAdapter, TransactionSynchronizationRegistry tsr, - XATerminator xaTerminator, String id, String description) { - this.ra = Objects.requireNonNull(resourceAdapter, "resourceAdapter cannot be null"); - this.tsr = Objects.requireNonNull(tsr, "tsr cannot be null"); - this.xaTerminator = Objects.requireNonNull(xaTerminator, "xaTerminator cannot be null"); + public IronJacamarVerticle(String id, String description, ResourceAdapter resourceAdapter, + CloneableBootstrapContext bootstrapContext) { this.id = Objects.requireNonNull(id, "id cannot be null"); this.description = Objects.requireNonNull(description, "description cannot be null"); + this.ra = Objects.requireNonNull(resourceAdapter, "resourceAdapter cannot be null"); + this.bootstrapContext = Objects.requireNonNull(bootstrapContext, "bootstrapContext cannot be null"); } @Override public void start() throws Exception { QuarkusIronJacamarLogger.log.startingResourceAdapter(id, description); - workManager = new QuarkusWorkManager(vertx); - // Create BootstrapContext - BootstrapContext bootstrapContext = new QuarkusBootstrapContext(workManager, tsr, xaTerminator); ra.start(bootstrapContext); } @Override - public void stop(Promise stopPromise) { + public void stop() { QuarkusIronJacamarLogger.log.stoppingResourceAdapter(id); - if (workManager != null) { - workManager.close().andThen((v) -> ra.stop()).andThen(stopPromise); - } else { - ra.stop(); - stopPromise.complete(); - } + ra.stop(); + // Shutdown the work manager + ((WorkManager) bootstrapContext.getWorkManager()).shutdown(); + bootstrapContext.shutdown(); } } diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusBootstrapContext.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusBootstrapContext.java deleted file mode 100644 index 63b278e..0000000 --- a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusBootstrapContext.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.quarkiverse.ironjacamar.runtime; - -import java.util.Timer; - -import jakarta.resource.spi.BootstrapContext; -import jakarta.resource.spi.UnavailableException; -import jakarta.resource.spi.XATerminator; -import jakarta.resource.spi.work.WorkContext; -import jakarta.resource.spi.work.WorkManager; -import jakarta.transaction.TransactionSynchronizationRegistry; - -class QuarkusBootstrapContext implements BootstrapContext { - - private final WorkManager workManager; - private final TransactionSynchronizationRegistry transactionSynchronizationRegistry; - private final XATerminator xaTerminator; - - public QuarkusBootstrapContext(WorkManager workManager, - TransactionSynchronizationRegistry transactionSynchronizationRegistry, - XATerminator xaTerminator) { - this.workManager = workManager; - this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; - this.xaTerminator = xaTerminator; - } - - @Override - public WorkManager getWorkManager() { - return workManager; - } - - @Override - public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { - return transactionSynchronizationRegistry; - } - - @Override - public XATerminator getXATerminator() { - return xaTerminator; - } - - @Override - public Timer createTimer() throws UnavailableException { - return new Timer("Quarkus JCA Timer", true); - } - - @Override - public boolean isContextSupported(Class workContextClass) { - return false; - } - -} diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusWorkManager.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusWorkManager.java deleted file mode 100644 index 7729bd5..0000000 --- a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/QuarkusWorkManager.java +++ /dev/null @@ -1,127 +0,0 @@ -package io.quarkiverse.ironjacamar.runtime; - -import jakarta.resource.spi.work.ExecutionContext; -import jakarta.resource.spi.work.Work; -import jakarta.resource.spi.work.WorkEvent; -import jakarta.resource.spi.work.WorkException; -import jakarta.resource.spi.work.WorkListener; -import jakarta.resource.spi.work.WorkManager; - -import io.quarkus.logging.Log; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.WorkerExecutor; - -/** - * Use Vert.x worker threads to execute JCA Work instances. - *

- * Note: The {@link WorkManager} provided in IronJacamar depends on JBoss Threads 2.4.0.Final, - * which is incompatible with Quarkus 3.x. - */ -class QuarkusWorkManager implements WorkManager { - - private final Vertx vertx; - - private final WorkerExecutor executor; - - public QuarkusWorkManager(Vertx vertx) { - this.vertx = vertx; - // TODO: Make the pool size configurable? Or use the Vert.x default? - this.executor = vertx.createSharedWorkerExecutor("jca-work-manager", 5); - } - - @Override - public void doWork(Work work) throws WorkException { - executor.executeBlocking(event -> { - work.run(); - }, result -> { - if (result.failed()) { - Log.error("Failed to execute work", result.cause()); - } - }); - } - - @Override - public void doWork(Work work, long startTimeout, ExecutionContext execContext, WorkListener workListener) - throws WorkException { - vertx.setTimer(startTimeout, id -> { - executor.executeBlocking(event -> { - workListener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED, work, null)); - work.run(); - }, result -> { - if (result.succeeded()) { - workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, null)); - } else { - Log.error("Failed to execute work", result.cause()); - workListener.workRejected( - new WorkEvent(this, WorkEvent.WORK_REJECTED, work, new WorkException(result.cause()))); - } - }); - }); - workListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null)); - } - - @Override - public long startWork(Work work) throws WorkException { - doWork(work); - return 0; - } - - @Override - public long startWork(Work work, long startTimeout, ExecutionContext execContext, WorkListener workListener) - throws WorkException { - vertx.setPeriodic(1L, startTimeout, id -> { - executor.executeBlocking(event -> { - workListener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED, work, null)); - work.run(); - }, result -> { - if (result.succeeded()) { - workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, null)); - } else { - Log.error("Failed to execute work", result.cause()); - workListener.workRejected( - new WorkEvent(this, WorkEvent.WORK_REJECTED, work, new WorkException(result.cause()))); - } - }); - }); - workListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null)); - return 0; - } - - @Override - public void scheduleWork(Work work) throws WorkException { - executor.executeBlocking(event -> { - work.run(); - }, result -> { - if (result.failed()) { - Log.error("Failed to execute work", result.cause()); - } - }); - } - - @Override - public void scheduleWork(Work work, long startTimeout, - ExecutionContext execContext, - WorkListener workListener) - throws WorkException { - vertx.setPeriodic(startTimeout, id -> { - executor.executeBlocking(event -> { - workListener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED, work, null)); - work.run(); - }, result -> { - if (result.succeeded()) { - workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, null)); - } else { - Log.error("Failed to execute work", result.cause()); - workListener.workRejected( - new WorkEvent(this, WorkEvent.WORK_REJECTED, work, new WorkException(result.cause()))); - } - }); - }); - workListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null)); - } - - public Future close() { - return executor.close(); - } -} diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusCallbackHandler.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusCallbackHandler.java new file mode 100644 index 0000000..fa96fe5 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusCallbackHandler.java @@ -0,0 +1,21 @@ +package io.quarkiverse.ironjacamar.runtime.security; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +public class QuarkusCallbackHandler implements CallbackHandler { + + private final org.jboss.jca.core.spi.security.Callback mappings; + + public QuarkusCallbackHandler(org.jboss.jca.core.spi.security.Callback mappings) { + this.mappings = mappings; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + mappings.mapCallbacks(callbacks); + } +} diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityContext.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityContext.java new file mode 100644 index 0000000..e9cc374 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityContext.java @@ -0,0 +1,24 @@ +package io.quarkiverse.ironjacamar.runtime.security; + +import javax.security.auth.Subject; + +import org.jboss.jca.core.spi.security.SecurityContext; + +public class QuarkusSecurityContext implements SecurityContext { + private Subject subject; + + @Override + public Subject getAuthenticatedSubject() { + return subject; + } + + @Override + public void setAuthenticatedSubject(Subject subject) { + this.subject = subject; + } + + @Override + public String[] getRoles() { + return new String[0]; + } +} diff --git a/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityIntegration.java b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityIntegration.java new file mode 100644 index 0000000..0675083 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/ironjacamar/runtime/security/QuarkusSecurityIntegration.java @@ -0,0 +1,37 @@ +package io.quarkiverse.ironjacamar.runtime.security; + +import javax.security.auth.callback.CallbackHandler; + +import org.jboss.jca.core.spi.security.Callback; +import org.jboss.jca.core.spi.security.SecurityContext; +import org.jboss.jca.core.spi.security.SecurityIntegration; + +public class QuarkusSecurityIntegration implements SecurityIntegration { + + private SecurityContext securityContext; + + @Override + public SecurityContext createSecurityContext(String sd) throws Exception { + return new QuarkusSecurityContext(); + } + + @Override + public SecurityContext getSecurityContext() { + return securityContext; + } + + @Override + public void setSecurityContext(SecurityContext context) { + this.securityContext = context; + } + + @Override + public CallbackHandler createCallbackHandler() { + return new QuarkusCallbackHandler(null); + } + + @Override + public CallbackHandler createCallbackHandler(Callback callback) { + return new QuarkusCallbackHandler(callback); + } +}