diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml index 31ae695d649dd..cb72b10fb197a 100644 --- a/bom/runtime/pom.xml +++ b/bom/runtime/pom.xml @@ -41,7 +41,7 @@ 1.0.11 1.0.9 1.0.9 - 1.0.3 + 1.0.6 3.20.9 1.1.1 1 diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/DotNames.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/DotNames.java new file mode 100644 index 0000000000000..8340a11ed6728 --- /dev/null +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/DotNames.java @@ -0,0 +1,31 @@ +package io.quarkus.smallrye.reactivemessaging.deployment; + +import org.eclipse.microprofile.reactive.messaging.Acknowledgment; +import org.eclipse.microprofile.reactive.messaging.Incoming; +import org.eclipse.microprofile.reactive.messaging.Outgoing; +import org.jboss.jandex.DotName; + +import io.smallrye.reactive.messaging.annotations.Broadcast; +import io.smallrye.reactive.messaging.annotations.Channel; +import io.smallrye.reactive.messaging.annotations.Emitter; +import io.smallrye.reactive.messaging.annotations.Merge; +import io.smallrye.reactive.messaging.annotations.OnOverflow; +import io.smallrye.reactive.messaging.annotations.Stream; + +public final class DotNames { + + static final DotName VOID = DotName.createSimple(void.class.getName()); + static final DotName INCOMING = DotName.createSimple(Incoming.class.getName()); + static final DotName OUTGOING = DotName.createSimple(Outgoing.class.getName()); + static final DotName CHANNEL = DotName.createSimple(Stream.class.getName()); + static final DotName STREAM = DotName.createSimple(Channel.class.getName()); + static final DotName EMITTER = DotName.createSimple(Emitter.class.getName()); + static final DotName ON_OVERFLOW = DotName.createSimple(OnOverflow.class.getName()); + static final DotName ACKNOWLEDGMENT = DotName.createSimple(Acknowledgment.class.getName()); + static final DotName MERGE = DotName.createSimple(Merge.class.getName()); + static final DotName BROADCAST = DotName.createSimple(Broadcast.class.getName()); + + private DotNames() { + } + +} diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/QuarkusMediatorConfigurationUtil.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/QuarkusMediatorConfigurationUtil.java new file mode 100644 index 0000000000000..17d1ec7730223 --- /dev/null +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/QuarkusMediatorConfigurationUtil.java @@ -0,0 +1,225 @@ +package io.quarkus.smallrye.reactivemessaging.deployment; + +import static io.quarkus.smallrye.reactivemessaging.deployment.DotNames.*; + +import java.util.List; + +import org.eclipse.microprofile.reactive.messaging.Acknowledgment; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationValue; +import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; + +import io.quarkus.arc.processor.BeanInfo; +import io.quarkus.deployment.recording.RecorderContext; +import io.quarkus.smallrye.reactivemessaging.runtime.QuarkusMediatorConfiguration; +import io.smallrye.reactive.messaging.Invoker; +import io.smallrye.reactive.messaging.MediatorConfigurationSupport; +import io.smallrye.reactive.messaging.Shape; +import io.smallrye.reactive.messaging.annotations.Merge; + +final class QuarkusMediatorConfigurationUtil { + + private QuarkusMediatorConfigurationUtil() { + } + + static QuarkusMediatorConfiguration create(MethodInfo methodInfo, BeanInfo bean, + String generatedInvokerName, RecorderContext recorderContext) { + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class returnTypeClass = load(methodInfo.returnType().name().toString(), cl); + Class[] parameterTypeClasses = new Class[methodInfo.parameters().size()]; + for (int i = 0; i < methodInfo.parameters().size(); i++) { + parameterTypeClasses[i] = load(methodInfo.parameters().get(i).name().toString(), cl); + } + + QuarkusMediatorConfiguration configuration = new QuarkusMediatorConfiguration(); + MediatorConfigurationSupport mediatorConfigurationSupport = new MediatorConfigurationSupport( + fullMethodName(methodInfo), returnTypeClass, parameterTypeClasses, + new ReturnTypeGenericTypeAssignable(methodInfo, cl), + methodInfo.parameters().isEmpty() ? new AlwaysInvalidIndexGenericTypeAssignable() + : new MethodParamGenericTypeAssignable(methodInfo, 0, cl)); + + configuration.setBeanId(bean.getIdentifier()); + configuration.setMethodName(methodInfo.name()); + + configuration.setInvokerClass((Class) recorderContext.classProxy(generatedInvokerName)); + String returnTypeName = methodInfo.returnType().name().toString(); + configuration.setReturnType(recorderContext.classProxy(returnTypeName)); + Class[] parameterTypes = new Class[methodInfo.parameters().size()]; + for (int i = 0; i < methodInfo.parameters().size(); i++) { + parameterTypes[i] = recorderContext.classProxy(methodInfo.parameters().get(i).name().toString()); + } + configuration.setParameterTypes(parameterTypes); + + String incomingValue = getValue(methodInfo, INCOMING); + configuration.setIncoming(incomingValue); + String outgoingValue = getValue(methodInfo, OUTGOING); + configuration.setOutgoing(outgoingValue); + + Shape shape = mediatorConfigurationSupport.determineShape(incomingValue, outgoingValue); + configuration.setShape(shape); + Acknowledgment.Strategy acknowledgment = mediatorConfigurationSupport.processSuppliedAcknowledgement(incomingValue, + () -> { + AnnotationInstance instance = methodInfo.annotation(ACKNOWLEDGMENT); + if (instance != null) { + return Acknowledgment.Strategy.valueOf(instance.value().asEnum()); + } + return null; + }); + configuration.setAcknowledgment(acknowledgment); + + MediatorConfigurationSupport.ValidationOutput validationOutput = mediatorConfigurationSupport.validate(shape, + acknowledgment); + configuration.setProduction(validationOutput.getProduction()); + configuration.setConsumption(validationOutput.getConsumption()); + if (validationOutput.getUseBuilderTypes() != null) { + configuration.setUseBuilderTypes(validationOutput.getUseBuilderTypes()); + } else { + configuration.setUseBuilderTypes(false); + } + + if (acknowledgment == null) { + acknowledgment = mediatorConfigurationSupport.processDefaultAcknowledgement(shape, + validationOutput.getConsumption()); + configuration.setAcknowledgment(acknowledgment); + } + + configuration.setMerge(mediatorConfigurationSupport.processMerge(incomingValue, () -> { + AnnotationInstance instance = methodInfo.annotation(MERGE); + if (instance != null) { + AnnotationValue value = instance.value(); + if (value == null) { + return Merge.Mode.MERGE; // the default value of @Merge + } + return Merge.Mode.valueOf(value.asEnum()); + } + return null; + })); + + configuration.setBroadcastValue(mediatorConfigurationSupport.processBroadcast(outgoingValue, () -> { + AnnotationInstance instance = methodInfo.annotation(BROADCAST); + if (instance != null) { + AnnotationValue value = instance.value(); + if (value == null) { + return 0; // the default value of @Broadcast + } + return value.asInt(); + } + return null; + })); + + return configuration; + } + + private static Class load(String className, ClassLoader cl) { + switch (className) { + case "boolean": + return boolean.class; + case "byte": + return byte.class; + case "short": + return short.class; + case "int": + return int.class; + case "long": + return long.class; + case "float": + return float.class; + case "double": + return double.class; + case "char": + return char.class; + case "void": + return void.class; + } + try { + return Class.forName(className, false, cl); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + } + + private static String getValue(MethodInfo methodInfo, DotName dotName) { + AnnotationInstance annotationInstance = methodInfo.annotation(dotName); + String value = null; + + if (annotationInstance != null) { + if (annotationInstance.value() != null) { + value = annotationInstance.value().asString(); + + } + if ((value == null) || value.isEmpty()) { + throw new IllegalArgumentException( + "@" + dotName.withoutPackagePrefix() + " value cannot be blank. Offending method is: " + + fullMethodName(methodInfo)); + } + + // TODO: does it make sense to validate the name with the supplied configuration? + } + + return value; + } + + private static String fullMethodName(MethodInfo methodInfo) { + return methodInfo.declaringClass() + "#" + methodInfo.name(); + } + + private static class ReturnTypeGenericTypeAssignable extends JandexGenericTypeAssignable { + + public ReturnTypeGenericTypeAssignable(MethodInfo method, ClassLoader classLoader) { + super(method.returnType(), classLoader); + } + } + + private static class JandexGenericTypeAssignable implements MediatorConfigurationSupport.GenericTypeAssignable { + + // will be used when we need to check assignability + private final ClassLoader classLoader; + private final Type type; + + public JandexGenericTypeAssignable(Type type, ClassLoader classLoader) { + this.classLoader = classLoader; + this.type = type; + } + + @Override + public Result check(Class target, int index) { + if (type.kind() != Type.Kind.PARAMETERIZED_TYPE) { + return Result.NotGeneric; + } + List arguments = type.asParameterizedType().arguments(); + if (arguments.size() >= index + 1) { + Class argumentClass = load(arguments.get(index).name().toString(), classLoader); + return argumentClass.isAssignableFrom(target) ? Result.Assignable : Result.NotAssignable; + } else { + return Result.InvalidIndex; + } + } + } + + private static class AlwaysInvalidIndexGenericTypeAssignable implements MediatorConfigurationSupport.GenericTypeAssignable { + + @Override + public Result check(Class target, int index) { + return Result.InvalidIndex; + } + } + + private static class MethodParamGenericTypeAssignable extends JandexGenericTypeAssignable { + + public MethodParamGenericTypeAssignable(MethodInfo method, int paramIndex, ClassLoader classLoader) { + super(getGenericParameterType(method, paramIndex), classLoader); + } + + private static Type getGenericParameterType(MethodInfo method, int paramIndex) { + List parameters = method.parameters(); + if (parameters.size() < paramIndex + 1) { + throw new IllegalArgumentException("Method " + method + " only has " + parameters.size() + + " so parameter with index " + paramIndex + " cannot be retrieved"); + } + return parameters.get(paramIndex); + } + } +} diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java index fbd271df629f9..cdc455c29e404 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java @@ -2,8 +2,9 @@ import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -12,8 +13,6 @@ import javax.enterprise.inject.spi.DeploymentException; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; -import org.eclipse.microprofile.reactive.messaging.Incoming; -import org.eclipse.microprofile.reactive.messaging.Outgoing; import org.eclipse.microprofile.reactive.messaging.spi.Connector; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; @@ -21,6 +20,7 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; import org.jboss.logging.Logger; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -36,16 +36,26 @@ import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.BuiltinScope; +import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.InjectionPointInfo; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem; +import io.quarkus.deployment.recording.RecorderContext; +import io.quarkus.deployment.util.HashUtil; +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.FieldDescriptor; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; +import io.quarkus.smallrye.reactivemessaging.runtime.QuarkusMediatorConfiguration; import io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle; import io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingRecorder; -import io.smallrye.reactive.messaging.annotations.Emitter; -import io.smallrye.reactive.messaging.annotations.OnOverflow; +import io.smallrye.reactive.messaging.Invoker; import io.smallrye.reactive.messaging.annotations.Stream; /** @@ -56,11 +66,7 @@ public class SmallRyeReactiveMessagingProcessor { private static final Logger LOGGER = Logger .getLogger("io.quarkus.smallrye-reactive-messaging.deployment.processor"); - static final DotName NAME_INCOMING = DotName.createSimple(Incoming.class.getName()); - static final DotName NAME_OUTGOING = DotName.createSimple(Outgoing.class.getName()); - static final DotName NAME_STREAM = DotName.createSimple(Stream.class.getName()); - static final DotName NAME_EMITTER = DotName.createSimple(Emitter.class.getName()); - static final DotName ON_OVERFLOW = DotName.createSimple(OnOverflow.class.getName()); + static final String INVOKER_SUFFIX = "_SmallryeMessagingInvoker"; @BuildStep FeatureBuildItem feature() { @@ -89,8 +95,8 @@ public void transform(AnnotationsTransformer.TransformationContext ctx) { } ClassInfo clazz = ctx.getTarget().asClass(); Map> annotations = clazz.annotations(); - if (annotations.containsKey(NAME_INCOMING) - || annotations.containsKey(NAME_OUTGOING)) { + if (annotations.containsKey(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.INCOMING) + || annotations.containsKey(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.OUTGOING)) { LOGGER.debugf( "Found reactive messaging annotations on a class %s with no scope defined - adding @Dependent", ctx.getTarget()); @@ -115,8 +121,10 @@ void validateBeanDeployment( if (bean.isClassBean()) { // TODO: add support for inherited business methods for (MethodInfo method : bean.getTarget().get().asClass().methods()) { - AnnotationInstance incoming = annotationStore.getAnnotation(method, NAME_INCOMING); - AnnotationInstance outgoing = annotationStore.getAnnotation(method, NAME_OUTGOING); + AnnotationInstance incoming = annotationStore.getAnnotation(method, + io.quarkus.smallrye.reactivemessaging.deployment.DotNames.INCOMING); + AnnotationInstance outgoing = annotationStore.getAnnotation(method, + io.quarkus.smallrye.reactivemessaging.deployment.DotNames.OUTGOING); if (incoming != null || outgoing != null) { if (incoming != null && incoming.value().asString().isEmpty()) { validationPhase.getContext().addDeploymentProblem( @@ -136,14 +144,21 @@ void validateBeanDeployment( for (InjectionPointInfo injectionPoint : validationPhase.getContext() .get(BuildExtension.Key.INJECTION_POINTS)) { - if (injectionPoint.getRequiredType().name().equals(NAME_EMITTER)) { - AnnotationInstance stream = injectionPoint.getRequiredQualifier(NAME_STREAM); - if (stream != null) { + if (injectionPoint.getRequiredType().name() + .equals(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.EMITTER)) { + AnnotationInstance instance = injectionPoint + .getRequiredQualifier(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.CHANNEL); + if (instance == null) { + instance = injectionPoint + .getRequiredQualifier(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.STREAM); //@Channel is the replacement of deprecated @Stream + } + if (instance != null) { // Stream.value() is mandatory - String name = stream.value().asString(); + String name = instance.value().asString(); Optional maybeOverflow = annotationStore.getAnnotations(injectionPoint.getTarget()) .stream() - .filter(ai -> ON_OVERFLOW.equals(ai.name())) + .filter(ai -> io.quarkus.smallrye.reactivemessaging.deployment.DotNames.ON_OVERFLOW + .equals(ai.name())) .filter(ai -> { if (ai.target().kind() == AnnotationTarget.Kind.METHOD_PARAMETER && injectionPoint.isParam()) { return ai.target().asMethodParameter().position() == injectionPoint.getPosition(); @@ -172,35 +187,58 @@ void validateBeanDeployment( @BuildStep public List removalExclusions() { - return Arrays.asList(new UnremovableBeanBuildItem(new BeanClassAnnotationExclusion(NAME_INCOMING)), - new UnremovableBeanBuildItem(new BeanClassAnnotationExclusion(NAME_OUTGOING))); + return Arrays.asList( + new UnremovableBeanBuildItem( + new BeanClassAnnotationExclusion(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.INCOMING)), + new UnremovableBeanBuildItem( + new BeanClassAnnotationExclusion(io.quarkus.smallrye.reactivemessaging.deployment.DotNames.OUTGOING))); } @BuildStep @Record(STATIC_INIT) - public void build(SmallRyeReactiveMessagingRecorder recorder, BeanContainerBuildItem beanContainer, + public void build(SmallRyeReactiveMessagingRecorder recorder, RecorderContext recorderContext, + BeanContainerBuildItem beanContainer, List mediatorMethods, List emitterFields, + BuildProducer generatedClass, BuildProducer reflectiveClass) { + + List configurations = new ArrayList<>(mediatorMethods.size()); + + ClassOutput classOutput = new ClassOutput() { + @Override + public void write(String name, byte[] data) { + generatedClass.produce(new GeneratedClassBuildItem(true, name, data)); + } + }; + /* - * IMPLEMENTATION NOTE/FUTURE IMPROVEMENTS: It would be possible to replace the reflection completely and use Jandex and - * generated - * io.smallrye.reactive.messaging.Invoker instead. However, we would have to mirror the logic from - * io.smallrye.reactive.messaging.MediatorConfiguration - * too. + * Go through the collected MediatorMethods and build up the corresponding MediaConfiguration + * This includes generating an invoker for each method + * The configuration will then be captured and used at static init time to push data into smallrye */ - Map beanClassToBeanId = new HashMap<>(); for (MediatorBuildItem mediatorMethod : mediatorMethods) { - String beanClass = mediatorMethod.getBean() - .getBeanClass() - .toString(); - if (!beanClassToBeanId.containsKey(beanClass)) { - reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, beanClass)); - beanClassToBeanId.put(beanClass, mediatorMethod.getBean() - .getIdentifier()); + MethodInfo methodInfo = mediatorMethod.getMethod(); + BeanInfo bean = mediatorMethod.getBean(); + + String generatedInvokerName = generateInvoker(bean, methodInfo, classOutput); + /* + * We need to register the invoker's constructor for reflection since it will be called inside smallrye. + * We could potentially lift this restriction with some extra CDI bean generation but it's probably not worth it + */ + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, generatedInvokerName)); + + try { + QuarkusMediatorConfiguration mediatorConfiguration = QuarkusMediatorConfigurationUtil.create(methodInfo, bean, + generatedInvokerName, recorderContext); + configurations.add(mediatorConfiguration); + } catch (IllegalArgumentException e) { + throw new DeploymentException(e); // needed to pass the TCK } } + recorder.registerMediators(configurations, beanContainer.getValue()); + for (EmitterBuildItem it : emitterFields) { int defaultBufferSize = ConfigProviderResolver.instance().getConfig() .getOptionalValue("smallrye.messaging.emitter.default-buffer-size", Integer.class).orElse(127); @@ -211,8 +249,83 @@ public void build(SmallRyeReactiveMessagingRecorder recorder, BeanContainerBuild recorder.configureEmitter(beanContainer.getValue(), it.getName(), null, 0, defaultBufferSize); } } + } + + /** + * Generates an invoker class that looks like the following: + * + *
+     * public class SomeName implements Invoker {
+     *     private Object beanInstance;
+     *
+     *     public SomeName(Object var1) {
+     *         this.beanInstance = var1;
+     *     }
+     *
+     *     public Object invoke(Object[] args) {
+     *         return ((BeanType) this.beanInstance).process(var1);
+     *     }
+     * }
+     * 
+ */ + private String generateInvoker(BeanInfo bean, MethodInfo method, ClassOutput classOutput) { + String baseName; + if (bean.getImplClazz().enclosingClass() != null) { + baseName = DotNames.simpleName(bean.getImplClazz().enclosingClass()) + "_" + + DotNames.simpleName(bean.getImplClazz().name()); + } else { + baseName = DotNames.simpleName(bean.getImplClazz().name()); + } + StringBuilder sigBuilder = new StringBuilder(); + sigBuilder.append(method.name()).append("_").append(method.returnType().name().toString()); + for (Type i : method.parameters()) { + sigBuilder.append(i.name().toString()); + } + String targetPackage = DotNames.packageName(bean.getImplClazz().name()); + String generatedName = targetPackage.replace('.', '/') + "/" + baseName + INVOKER_SUFFIX + "_" + method.name() + "_" + + HashUtil.sha1(sigBuilder.toString()); + + try (ClassCreator invoker = ClassCreator.builder().classOutput(classOutput).className(generatedName) + .interfaces(Invoker.class) + .build()) { + + FieldDescriptor beanInstanceField = invoker.getFieldCreator("beanInstance", Object.class).getFieldDescriptor(); + + // generate a constructor that bean instance an argument + try (MethodCreator ctor = invoker.getMethodCreator("", void.class, Object.class)) { + ctor.setModifiers(Modifier.PUBLIC); + ctor.invokeSpecialMethod(MethodDescriptor.ofConstructor(Object.class), ctor.getThis()); + ResultHandle self = ctor.getThis(); + ResultHandle config = ctor.getMethodParam(0); + ctor.writeInstanceField(beanInstanceField, self, config); + ctor.returnValue(null); + } + + try (MethodCreator invoke = invoker.getMethodCreator( + MethodDescriptor.ofMethod(generatedName, "invoke", Object.class, Object[].class))) { + + int parametersCount = method.parameters().size(); + String[] argTypes = new String[parametersCount]; + ResultHandle[] args = new ResultHandle[parametersCount]; + for (int i = 0; i < parametersCount; i++) { + // the only method argument of io.smallrye.reactive.messaging.Invoker is an object array so we need to pull out + // each argument and put it in the target method arguments array + args[i] = invoke.readArrayValue(invoke.getMethodParam(0), i); + argTypes[i] = method.parameters().get(i).name().toString(); + } + ResultHandle result = invoke.invokeVirtualMethod( + MethodDescriptor.ofMethod(method.declaringClass().name().toString(), method.name(), + method.returnType().name().toString(), argTypes), + invoke.readInstanceField(beanInstanceField, invoke.getThis()), args); + if (io.quarkus.smallrye.reactivemessaging.deployment.DotNames.VOID.equals(method.returnType().name())) { + invoke.returnValue(invoke.loadNull()); + } else { + invoke.returnValue(result); + } + } + } - recorder.registerMediators(beanClassToBeanId, beanContainer.getValue()); + return generatedName.replace('/', '.'); } } diff --git a/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/QuarkusMediatorConfiguration.java b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/QuarkusMediatorConfiguration.java new file mode 100644 index 0000000000000..30eee3ae1d115 --- /dev/null +++ b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/QuarkusMediatorConfiguration.java @@ -0,0 +1,211 @@ +package io.quarkus.smallrye.reactivemessaging.runtime; + +import java.lang.reflect.Method; + +import javax.enterprise.inject.spi.Bean; + +import org.eclipse.microprofile.reactive.messaging.Acknowledgment; + +import io.quarkus.arc.Arc; +import io.smallrye.reactive.messaging.Invoker; +import io.smallrye.reactive.messaging.MediatorConfiguration; +import io.smallrye.reactive.messaging.Shape; +import io.smallrye.reactive.messaging.annotations.Merge; + +public class QuarkusMediatorConfiguration implements MediatorConfiguration { + + private String beanId; + + private String methodName; + + private Class returnType; + + private Class[] parameterTypes; + + private Shape shape; + + private String incoming; + + private String outgoing; + + private Acknowledgment.Strategy acknowledgment; + + private Integer broadcastValue; + + private MediatorConfiguration.Production production = MediatorConfiguration.Production.NONE; + + private MediatorConfiguration.Consumption consumption = MediatorConfiguration.Consumption.NONE; + + private boolean useBuilderTypes = false; + + private Merge.Mode merge; + + private Class invokerClass; + + public String getBeanId() { + return beanId; + } + + public void setBeanId(String beanId) { + this.beanId = beanId; + } + + @Override + public Bean getBean() { + return Arc.container().bean(beanId); + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + @Override + public Class getReturnType() { + return returnType; + } + + public void setReturnType(Class returnType) { + this.returnType = returnType; + } + + @Override + public Class[] getParameterTypes() { + return parameterTypes; + } + + public void setParameterTypes(Class[] parameterTypes) { + this.parameterTypes = parameterTypes; + } + + public Shape getShape() { + return shape; + } + + public void setShape(Shape shape) { + this.shape = shape; + } + + @Override + public Shape shape() { + return shape; + } + + @Override + public String getIncoming() { + return incoming; + } + + public void setIncoming(String incoming) { + this.incoming = incoming; + } + + @Override + public String getOutgoing() { + return outgoing; + } + + public void setOutgoing(String outgoing) { + this.outgoing = outgoing; + } + + @Override + public Acknowledgment.Strategy getAcknowledgment() { + return acknowledgment; + } + + public void setAcknowledgment(Acknowledgment.Strategy acknowledgment) { + this.acknowledgment = acknowledgment; + } + + public Integer getBroadcastValue() { + return broadcastValue; + } + + public void setBroadcastValue(Integer broadcastValue) { + this.broadcastValue = broadcastValue; + } + + @Override + public boolean getBroadcast() { + return broadcastValue != null; + } + + public MediatorConfiguration.Production getProduction() { + return production; + } + + public void setProduction(MediatorConfiguration.Production production) { + this.production = production; + } + + @Override + public MediatorConfiguration.Production production() { + return production; + } + + public MediatorConfiguration.Consumption getConsumption() { + return consumption; + } + + public void setConsumption(MediatorConfiguration.Consumption consumption) { + this.consumption = consumption; + } + + @Override + public MediatorConfiguration.Consumption consumption() { + return consumption; + } + + public boolean isUseBuilderTypes() { + return useBuilderTypes; + } + + public void setUseBuilderTypes(boolean useBuilderTypes) { + this.useBuilderTypes = useBuilderTypes; + } + + @Override + public boolean usesBuilderTypes() { + return useBuilderTypes; + } + + public Merge.Mode getMerge() { + return merge; + } + + public void setMerge(Merge.Mode merge) { + this.merge = merge; + } + + @Override + public Class getInvokerClass() { + return invokerClass; + } + + public void setInvokerClass(Class invokerClass) { + this.invokerClass = invokerClass; + } + + @Override + public String methodAsString() { + return getBean().getBeanClass().getName() + "#" + getMethodName(); + } + + @Override + public Method getMethod() { + throw new UnsupportedOperationException("getMethod is not meant to be called on " + this.getClass().getName()); + } + + @Override + public int getNumberOfSubscriberBeforeConnecting() { + if (!getBroadcast()) { + return -1; + } else { + return broadcastValue; + } + } +} diff --git a/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/SmallRyeReactiveMessagingRecorder.java b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/SmallRyeReactiveMessagingRecorder.java index c44544d6337c7..f2d670c027edd 100644 --- a/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/SmallRyeReactiveMessagingRecorder.java +++ b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/SmallRyeReactiveMessagingRecorder.java @@ -1,11 +1,7 @@ package io.quarkus.smallrye.reactivemessaging.runtime; -import java.util.Map; -import java.util.Map.Entry; +import java.util.List; -import javax.enterprise.inject.spi.DeploymentException; - -import io.quarkus.arc.Arc; import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.runtime.annotations.Recorder; import io.smallrye.reactive.messaging.extension.MediatorManager; @@ -22,22 +18,9 @@ public void configureEmitter(BeanContainer container, String name, String strate mediatorManager.initializeEmitter(name, strategy, bufferSize, defaultBufferSize); } - public void registerMediators(Map beanClassToBeanId, BeanContainer container) { + public void registerMediators(List configurations, BeanContainer container) { MediatorManager mediatorManager = container.instance(MediatorManager.class); - for (Entry entry : beanClassToBeanId.entrySet()) { - try { - Class beanClass = Thread.currentThread() - .getContextClassLoader() - .loadClass(entry.getKey()); - mediatorManager.analyze(beanClass, Arc.container() - .bean(entry.getValue())); - } catch (ClassNotFoundException e) { - throw new IllegalStateException(e); - } catch (IllegalArgumentException e) { - throw new DeploymentException(e); - } - } - + mediatorManager.addAnalyzed(configurations); } }