com.github.jonasrutishauser.transactional.event.quarkus.deployment.TransactionalEventBuildCompatibleExtension Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of transactional-event-quarkus-deployment Show documentation
Show all versions of transactional-event-quarkus-deployment Show documentation
Library for transactional event publishing und listening
package com.github.jonasrutishauser.transactional.event.quarkus.deployment;
import static jakarta.interceptor.Interceptor.Priority.LIBRARY_AFTER;
import static java.util.function.Predicate.isEqual;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import com.github.jonasrutishauser.jakarta.enterprise.inject.ExtendedInstance;
import com.github.jonasrutishauser.transactional.event.api.handler.AbstractHandler;
import com.github.jonasrutishauser.transactional.event.api.handler.EventHandler;
import com.github.jonasrutishauser.transactional.event.api.handler.Handler;
import com.github.jonasrutishauser.transactional.event.api.serialization.EventDeserializer;
import com.github.jonasrutishauser.transactional.event.core.cdi.DefaultEventDeserializer;
import com.github.jonasrutishauser.transactional.event.core.cdi.ExtendedEventDeserializer;
import com.github.jonasrutishauser.transactional.event.core.handler.EventHandlers;
import com.github.jonasrutishauser.transactional.event.core.store.Dispatcher;
import com.github.jonasrutishauser.transactional.event.quarkus.DefaultEventDeserializerCreator;
import com.github.jonasrutishauser.transactional.event.quarkus.ExtendedInstanceCreator;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Initialized;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Default;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.build.compatible.spi.BeanInfo;
import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
import jakarta.enterprise.inject.build.compatible.spi.ClassConfig;
import jakarta.enterprise.inject.build.compatible.spi.Enhancement;
import jakarta.enterprise.inject.build.compatible.spi.InjectionPointInfo;
import jakarta.enterprise.inject.build.compatible.spi.InvokerFactory;
import jakarta.enterprise.inject.build.compatible.spi.InvokerInfo;
import jakarta.enterprise.inject.build.compatible.spi.Messages;
import jakarta.enterprise.inject.build.compatible.spi.MethodConfig;
import jakarta.enterprise.inject.build.compatible.spi.ParameterConfig;
import jakarta.enterprise.inject.build.compatible.spi.Registration;
import jakarta.enterprise.inject.build.compatible.spi.Synthesis;
import jakarta.enterprise.inject.build.compatible.spi.SyntheticBeanBuilder;
import jakarta.enterprise.inject.build.compatible.spi.SyntheticBeanCreator;
import jakarta.enterprise.inject.build.compatible.spi.SyntheticComponents;
import jakarta.enterprise.inject.build.compatible.spi.Types;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ClassInfo;
import jakarta.enterprise.lang.model.declarations.MethodInfo;
import jakarta.enterprise.lang.model.types.ClassType;
import jakarta.enterprise.lang.model.types.ParameterizedType;
import jakarta.enterprise.lang.model.types.Type;
import jakarta.inject.Singleton;
public class TransactionalEventBuildCompatibleExtension implements BuildCompatibleExtension {
private final Set requiredEventDeserializers = new HashSet<>();
private final Set declaredEventDeserializers = new HashSet<>();
private final Map handlerClass = new HashMap<>();
private MethodInfo startupMethod;
private InvokerInfo startup;
private InvokerInfo extendedInstanceProducer;
private Map> requiredExtendedInstances = new HashMap<>();
@Enhancement(types = Object.class, withSubtypes = true)
public void createLockOwnerOnlyOnce(ClassConfig type) {
if ("com.github.jonasrutishauser.transactional.event.core.store.LockOwner".equals(type.info().name())) {
type.removeAnnotation(annotation -> ApplicationScoped.class.getName().equals(annotation.name()));
type.addAnnotation(Singleton.class);
}
}
@Enhancement(types = Dispatcher.class, withSubtypes = true)
public void disableStaticInitStartup(MethodConfig method) {
if (!method.parameters().isEmpty()) {
ParameterConfig firstParameter = method.parameters().get(0);
if (firstParameter.info().hasAnnotation(Observes.class) && firstParameter.info()
.hasAnnotation(annotation -> Initialized.class.getName().equals(annotation.name())
&& annotation.value().asType().isClass() && ApplicationScoped.class.getName()
.equals(annotation.value().asType().asClass().declaration().name()))) {
firstParameter.removeAllAnnotations();
startupMethod = method.info();
}
}
}
@Registration(types = Dispatcher.class)
public void getStartupBean(BeanInfo beanInfo, InvokerFactory invokerFactory) {
if (startupMethod != null && beanInfo.declaringClass().methods().contains(startupMethod)) {
startup = invokerFactory.createInvoker(beanInfo, startupMethod).withInstanceLookup().build();
}
}
@Registration(types = Handler.class)
public void processHandlers(BeanInfo beanInfo, Messages messages) {
Optional eventHandlerAnnotation = beanInfo.qualifiers().stream()
.filter(annotation -> EventHandler.class.getName().equals(annotation.name())).findAny();
if (eventHandlerAnnotation.isEmpty()) {
messages.error("EventHandler annotation is missing on bean", beanInfo);
} else {
if (EventHandler.ABSTRACT_HANDLER_TYPE.equals(eventHandlerAnnotation.get().member("eventType").asString())) {
Optional abstractHandlerType = getAbstractHandlerType(beanInfo.types());
if (abstractHandlerType.isEmpty()) {
messages.error("AbstractHandler type is missing on bean with implicit event type", beanInfo);
} else if (beanInfo.types().stream().filter(Type::isClass).map(Type::asClass).map(ClassType::declaration)
.noneMatch(isEqual(beanInfo.declaringClass()))) {
messages.error(beanInfo.declaringClass().simpleName() + " type is missing on bean with implicit event type", beanInfo);
} else {
handlerClass.put(getClassInfo(abstractHandlerType.get().typeArguments().get(0)), beanInfo.declaringClass());
}
}
}
}
@Synthesis
public void addEventHandlersBean(SyntheticComponents components) throws ClassNotFoundException {
List types = new ArrayList<>();
List beans = new ArrayList<>();
handlerClass.forEach((type, bean) -> {
types.add(type);
beans.add(bean);
});
Class> quarkusEventHandlers = Class.forName("com.github.jonasrutishauser.transactional.event.quarkus.handler.QuarkusEventHandlers");
addCreator(components.addBean(quarkusEventHandlers) //
.type(EventHandlers.class) //
.type(quarkusEventHandlers) //
.scope(Singleton.class) //
.withParam("types", types.toArray(ClassInfo[]::new)) //
.withParam("beans", beans.toArray(ClassInfo[]::new)) //
.withParam("startup", startup), quarkusEventHandlers);
}
@SuppressWarnings("unchecked")
private void addCreator(SyntheticBeanBuilder builder, Class> creatorHost) {
builder.createWith(
Arrays.stream(creatorHost.getNestMembers()).filter(SyntheticBeanCreator.class::isAssignableFrom)
.map(c -> (Class extends SyntheticBeanCreator>) c).findAny()
.orElseThrow(IllegalStateException::new));
}
@Registration(types = Object.class)
public void getExtendedInstanceProducer(BeanInfo beanInfo, Types types, InvokerFactory invokerFactory) {
if (beanInfo.isClassBean()) {
Optional producer = beanInfo.declaringClass().methods().stream()
.filter(method -> method.returnType().isParameterizedType()
&& types.of(ExtendedInstance.class)
.equals(method.returnType().asParameterizedType().genericClass())
&& method.parameters().size() == 3
&& types.of(BeanManager.class).equals(method.parameters().get(0).type())
&& method.parameters().get(2).type().isParameterizedType()
&& types.of(Instance.class)
.equals(method.parameters().get(2).type().asParameterizedType().genericClass()))
.findAny();
producer.ifPresent(
method -> extendedInstanceProducer = invokerFactory.createInvoker(beanInfo, method).build());
}
for (InjectionPointInfo injectionPoint : beanInfo.injectionPoints()) {
Type type = injectionPoint.type();
if ((type.isClass() || type.isParameterizedType())
&& ExtendedInstance.class.getName().equals(getClassInfo(type).name())) {
Type simplifiedType = types.of(Object.class);
if (type.isParameterizedType()) {
Type typeArgument = type.asParameterizedType().typeArguments().get(0);
if (!typeArgument.isTypeVariable() && !typeArgument.isWildcardType()) {
simplifiedType = typeArgument;
}
}
requiredExtendedInstances.computeIfAbsent(simplifiedType, key -> new HashSet<>())
.addAll(injectionPoint.qualifiers());
}
}
}
@Synthesis
public void registerExtendedInstanceBeans(SyntheticComponents components, Types types) {
if (extendedInstanceProducer != null) {
for (Entry> extendedInstance : requiredExtendedInstances.entrySet()) {
@SuppressWarnings("rawtypes")
SyntheticBeanBuilder builder = components.addBean(ExtendedInstance.class)
.type(types.parameterized(ExtendedInstance.class, extendedInstance.getKey())) //
.alternative(true) //
.priority(LIBRARY_AFTER);
extendedInstance.getValue().forEach(builder::qualifier);
builder.createWith(ExtendedInstanceCreator.class) //
.withParam(ExtendedInstanceCreator.PRODUCER, extendedInstanceProducer)
.withParam(ExtendedInstanceCreator.TYPE, getClassInfo(extendedInstance.getKey()));
}
}
}
@Registration(types = Object.class)
public void processEventDeserializerInjections(BeanInfo beanInfo, Types types, Messages messages) {
for (InjectionPointInfo injectionPoint : beanInfo.injectionPoints()) {
Type type = injectionPoint.type();
if (type.isParameterizedType()
&& EventDeserializer.class.getName().equals(type.asParameterizedType().declaration().name())) {
requiredEventDeserializers.add(type.asParameterizedType());
}
}
}
@Registration(types = EventDeserializer.class)
public void processEventDeserializers(BeanInfo beanInfo, Messages messages) {
declaredEventDeserializers.addAll(beanInfo.types());
}
@Synthesis
@Priority(LIBRARY_AFTER)
public void addMissingEventDeserializers(SyntheticComponents components) {
requiredEventDeserializers.removeAll(declaredEventDeserializers);
for (ParameterizedType type : requiredEventDeserializers) {
Type eventType = type.typeArguments().get(0);
ClassType eventClass = eventType.isClass() ? eventType.asClass()
: eventType.asParameterizedType().genericClass();
components.addBean(DefaultEventDeserializer.class) //
.type(type) //
.type(ExtendedEventDeserializer.class) //
.scope(Singleton.class) //
.qualifier(Default.Literal.INSTANCE) //
.createWith(DefaultEventDeserializerCreator.class) //
.withParam(DefaultEventDeserializerCreator.TYPE, eventClass.declaration());
}
}
private ClassInfo getClassInfo(Type type) {
if (type.isParameterizedType()) {
return type.asParameterizedType().declaration();
}
return type.asClass().declaration();
}
private Optional getAbstractHandlerType(Collection types) {
return types.stream() //
.filter(Type::isParameterizedType) //
.map(Type::asParameterizedType) //
.filter(type -> AbstractHandler.class.getName().equals(type.declaration().name())) //
.findAny();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy