Skip to content

Commit

Permalink
@TestTransaction - move logic to the TestTransactionInterceptor class
Browse files Browse the repository at this point in the history
    - add basic support for interceptor's inheritance
    - other unsupported use cases are described here:
    quarkusio/quarkus#11942
  • Loading branch information
Luca Di Grazia committed Sep 4, 2022
1 parent fb9f66a commit 60c9755
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,26 @@
import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean;
import com.arjuna.ats.internal.arjuna.coordinator.CheckedActionFactoryImple;
import com.arjuna.ats.internal.arjuna.objectstore.ShadowNoFileLockStore;
import com.arjuna.ats.internal.arjuna.utils.SocketProcessId;
import com.arjuna.ats.internal.jta.recovery.arjunacore.CommitMarkableResourceRecordRecoveryModule;
import com.arjuna.ats.internal.jta.recovery.arjunacore.RecoverConnectableAtomicAction;
import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple;
import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple;
import com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple;
import com.arjuna.ats.jta.common.JTAEnvironmentBean;
import com.arjuna.common.util.propertyservice.PropertiesFactory;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem;
import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem.ContextConfiguratorBuildItem;
import io.quarkus.arc.deployment.CustomScopeBuildItem;
import io.quarkus.arc.deployment.ContextRegistrarBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.ContextRegistrar;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsTest;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CapabilityBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
Expand Down Expand Up @@ -61,33 +58,31 @@ public NativeImageSystemPropertyBuildItem nativeImageSystemPropertyBuildItem() {
return new NativeImageSystemPropertyBuildItem("CoordinatorEnvironmentBean.transactionStatusManagerEnable", "false");
}

@BuildStep
CapabilityBuildItem capability() {
return new CapabilityBuildItem(Capability.TRANSACTIONS);
}

@BuildStep
@Record(RUNTIME_INIT)
public void build(NarayanaJtaRecorder recorder,
BuildProducer<AdditionalBeanBuildItem> additionalBeans,
BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<RuntimeInitializedClassBuildItem> runtimeInit,
BuildProducer<FeatureBuildItem> feature,
TransactionManagerConfiguration transactions, ShutdownContextBuildItem shutdownContextBuildItem) {
recorder.handleShutdown(shutdownContextBuildItem);
TransactionManagerConfiguration transactions) {
feature.produce(new FeatureBuildItem(Feature.NARAYANA_JTA));
additionalBeans.produce(new AdditionalBeanBuildItem(NarayanaJtaProducers.class));
additionalBeans.produce(new AdditionalBeanBuildItem(CDIDelegatingTransactionManager.class));

runtimeInit.produce(new RuntimeInitializedClassBuildItem(
"com.arjuna.ats.internal.jta.resources.arjunacore.CommitMarkableResourceRecord"));
runtimeInit.produce(new RuntimeInitializedClassBuildItem(SocketProcessId.class.getName()));
runtimeInit.produce(new RuntimeInitializedClassBuildItem(CommitMarkableResourceRecordRecoveryModule.class.getName()));
runtimeInit.produce(new RuntimeInitializedClassBuildItem(RecoverConnectableAtomicAction.class.getName()));

reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, JTAEnvironmentBean.class.getName(),
UserTransactionImple.class.getName(),
CheckedActionFactoryImple.class.getName(),
TransactionManagerImple.class.getName(),
TransactionSynchronizationRegistryImple.class.getName(),
ObjectStoreEnvironmentBean.class.getName(),
ShadowNoFileLockStore.class.getName(),
SocketProcessId.class.getName()));
ShadowNoFileLockStore.class.getName()));

AdditionalBeanBuildItem.Builder builder = AdditionalBeanBuildItem.builder();
builder.addBeanClass(TransactionalInterceptorSupports.class);
Expand All @@ -110,7 +105,6 @@ public void build(NarayanaJtaRecorder recorder,
recorder.disableTransactionStatusManager();
recorder.setNodeName(transactions);
recorder.setDefaultTimeout(transactions);
recorder.setConfig(transactions);
}

@BuildStep(onlyIf = IsTest.class)
Expand All @@ -132,14 +126,15 @@ void testTx(BuildProducer<GeneratedBeanBuildItem> generatedBeanBuildItemBuildPro
}

@BuildStep
public ContextConfiguratorBuildItem transactionContext(ContextRegistrationPhaseBuildItem contextRegistrationPhase) {
return new ContextConfiguratorBuildItem(contextRegistrationPhase.getContext()
.configure(TransactionScoped.class).normal().contextClass(TransactionContext.class));
}

@BuildStep
public CustomScopeBuildItem registerScope() {
return new CustomScopeBuildItem(TransactionScoped.class);
public void transactionContext(
BuildProducer<ContextRegistrarBuildItem> contextRegistry) {

contextRegistry.produce(new ContextRegistrarBuildItem(new ContextRegistrar() {
@Override
public void register(RegistrationContext registrationContext) {
registrationContext.configure(TransactionScoped.class).normal().contextClass(TransactionContext.class).done();
}
}, TransactionScoped.class));
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.transaction.Status;
import javax.transaction.UserTransaction;

import io.quarkus.narayana.jta.runtime.test.TestTransactionCallback;
Expand All @@ -29,14 +28,6 @@ public class TestTransactionInterceptor {

@AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
// do nothing in case there is already a transaction (e.g. self-intercepted non-private non-test method in test class)
// w/o this check userTransaction.begin() would fail because there is already a tx associated with the current thread
if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) {
return context.proceed();
}

// an exception from proceed() has to be captured to avoid shadowing it in finally() with a exception from rollback()
Throwable caught = null;
try {
userTransaction.begin();
for (TestTransactionCallback i : CALLBACKS) {
Expand All @@ -47,19 +38,9 @@ public Object intercept(InvocationContext context) throws Exception {
i.preRollback();
}
return result;
} catch (Exception | Error e) { // note: "Error" shall mainly address AssertionError
caught = e;
throw e;
} finally {
if (caught == null) {
userTransaction.rollback();
} else {
try {
userTransaction.rollback();
} catch (Exception e) {
caught.addSuppressed(e);
}
}
userTransaction.rollback();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ public class InterceptorInfo extends BeanInfo implements Comparable<InterceptorI
List<MethodInfo> preDestroys = new ArrayList<>();

ClassInfo aClass = target.asClass();
Set<DotName> scanned = new HashSet<>();
while (aClass != null) {
if (!scanned.add(aClass.name())) {
continue;
}
for (MethodInfo method : aClass.methods()) {
if (Modifier.isStatic(method.flags())) {
continue;
Expand Down Expand Up @@ -91,7 +95,7 @@ public class InterceptorInfo extends BeanInfo implements Comparable<InterceptorI

DotName superTypeName = aClass.superName();
aClass = superTypeName == null || DotNames.OBJECT.equals(superTypeName) ? null
: getClassByName(beanDeployment.getBeanArchiveIndex(), superTypeName);
: getClassByName(beanDeployment.getIndex(), superTypeName);
}

this.aroundInvoke = aroundInvokes.isEmpty() ? null : aroundInvokes.get(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.arc.test.interceptors.inheritance;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@One
@Interceptor
public class Interceptor1 extends OverridenInterceptor {

@AroundInvoke
@Override
public Object intercept(InvocationContext ctx) throws Exception {
return ctx.proceed() + "1";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.quarkus.arc.test.interceptors.inheritance;

import javax.interceptor.Interceptor;

@Two
@Interceptor
public class Interceptor2 extends OverridenInterceptor {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.arc.test.interceptors.inheritance;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.quarkus.arc.Arc;
import io.quarkus.arc.test.ArcTestContainer;
import javax.enterprise.context.ApplicationScoped;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

/**
* "Around-invoke methods may be defined on interceptor classes and/or the target class and/or super-
* classes of the target class or the interceptor classes. However, only one around-invoke method may be
* defined on a given class."
*/
public class InterceptorSuperclassTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(Interceptor1.class, Interceptor2.class, One.class, Two.class,
OverridenInterceptor.class, Fool.class);

@Test
public void testInterception() {
Fool fool = Arc.container().instance(Fool.class).get();
assertEquals("ping1", fool.pingOne());
assertEquals("pingoverriden", fool.pingTwo());
}

@ApplicationScoped
public static class Fool {

@One
String pingOne() {
return "ping";
}

@Two
String pingTwo() {
return "ping";
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.arc.test.interceptors.inheritance;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
@InterceptorBinding
public @interface One {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.arc.test.interceptors.inheritance;

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class OverridenInterceptor {

@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
return ctx.proceed() + "overriden";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.arc.test.interceptors.inheritance;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
@InterceptorBinding
public @interface Two {

}

0 comments on commit 60c9755

Please sign in to comment.