diff --git a/.gitignore b/.gitignore index 5f2dbe1..67d0d9f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ buildNumber.properties # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) !/.mvn/wrapper/maven-wrapper.jar + +# IntelliJ stuff +.idea/ +*.iml diff --git a/README.md b/README.md index 1091391..5bdf3f6 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ You can provide configuration overrides for the following Axon artifacts by crea For more details on these objects and the Axon Framework, please consult the [Axon Framework Reference Guide](https://docs.axonframework.org). ### Aggregates -You can define aggregate roots by placing a simple annotation `org.axonframework.cdi.stereotype.Aggregate` on your class. It will be automatically collected by the CDI container and registered. +You can define aggregate roots by placing a simple annotation `org.axonframework.extensions.cdi.stereotype.Aggregate` on your class. It will be automatically collected by the CDI container and registered. ### Event Handlers and Query Handlers Event handlers and query handlers must be CDI beans. They will be automatically registered with Axon for you. diff --git a/cdi/pom.xml b/cdi/pom.xml index 4a26050..b279b77 100644 --- a/cdi/pom.xml +++ b/cdi/pom.xml @@ -15,9 +15,7 @@ ~ limitations under the License. --> - + 4.0.0 axon-cdi-parent @@ -27,16 +25,10 @@ axon-cdi - Axon Framework CDI Extension - - This module contains components that integrate with CDI. - - - jar + Axon Framework CDI Support + Provides official Axon support for the CDI programming model. - 1.1.11 - 1.8.2 @@ -44,67 +36,35 @@ org.axonframework axon-configuration - ${axon.version} - - - - javax.enterprise - cdi-api - provided - javax.transaction - javax.transaction-api - provided - - - - org.apache.commons - commons-lang3 - 3.8.1 + org.axonframework + axon-test + test - - org.apache.deltaspike.core - deltaspike-core-api - ${deltaspike.version} - - - org.apache.deltaspike.core - deltaspike-core-impl - ${deltaspike.version} + javax + javaee-api + provided - - - ch.qos.logback - logback-classic - test - - - org.slf4j - jul-to-slf4j - test - org.slf4j - slf4j-log4j12 - test + slf4j-api + + org.slf4j - jcl-over-slf4j + slf4j-simple test - log4j - log4j + junit + junit test - - - org.jboss.weld.se weld-se-core @@ -115,26 +75,6 @@ weld-junit4 test - - - junit - junit - test - - - - org.mockito - mockito-core - test - - - - commons-io - commons-io - 1.3.2 - test - - - \ No newline at end of file + diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/AggregateDefinition.java b/cdi/src/main/java/org/axonframework/extensions/cdi/AggregateDefinition.java index bb3d25d..9efad70 100644 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/AggregateDefinition.java +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/AggregateDefinition.java @@ -1,7 +1,6 @@ package org.axonframework.extensions.cdi; import org.axonframework.extensions.cdi.stereotype.Aggregate; -import org.axonframework.extensions.cdi.util.StringUtilities; import java.lang.annotation.Annotation; import java.util.Arrays; diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/AxonCdiExtension.java b/cdi/src/main/java/org/axonframework/extensions/cdi/AxonCdiExtension.java index 8ac4220..01809ea 100644 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/AxonCdiExtension.java +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/AxonCdiExtension.java @@ -1,25 +1,31 @@ package org.axonframework.extensions.cdi; -import org.apache.commons.lang3.reflect.TypeUtils; -import org.apache.deltaspike.core.util.bean.BeanBuilder; -import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider; +import org.axonframework.extensions.cdi.stereotype.Aggregate; +import org.axonframework.extensions.cdi.stereotype.Saga; +import org.axonframework.commandhandling.CommandBus; import org.axonframework.common.jpa.EntityManagerProvider; import org.axonframework.common.lock.LockFactory; import org.axonframework.common.lock.NullLockFactory; import org.axonframework.common.transaction.TransactionManager; import org.axonframework.config.*; +import org.axonframework.deadline.DeadlineManager; +import org.axonframework.eventhandling.ErrorHandler; import org.axonframework.eventhandling.EventBus; +import org.axonframework.eventhandling.ListenerInvocationErrorHandler; +import org.axonframework.eventhandling.tokenstore.TokenStore; import org.axonframework.eventsourcing.SnapshotTriggerDefinition; import org.axonframework.eventsourcing.eventstore.EventStorageEngine; -import org.axonframework.extensions.cdi.config.ConfigurerProducer; -import org.axonframework.extensions.cdi.config.IfNoneDefined; -import org.axonframework.extensions.cdi.messagehandling.MessageHandlingBeanDefinition; -import org.axonframework.extensions.cdi.stereotype.Aggregate; -import org.axonframework.extensions.cdi.util.CdiUtilities; -import org.axonframework.extensions.cdi.util.SupplierBasedLifeCycle; +import org.axonframework.messaging.correlation.CorrelationDataProvider; import org.axonframework.modelling.command.CommandTargetResolver; import org.axonframework.modelling.command.GenericJpaRepository; import org.axonframework.modelling.command.Repository; +import org.axonframework.modelling.saga.ResourceInjector; +import org.axonframework.modelling.saga.repository.SagaStore; +import org.axonframework.queryhandling.QueryBus; +import org.axonframework.queryhandling.QueryGateway; +import org.axonframework.queryhandling.QueryUpdateEmitter; +import org.axonframework.serialization.Serializer; +import org.axonframework.serialization.upcasting.event.EventUpcaster; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,24 +33,20 @@ import javax.enterprise.context.Destroyed; import javax.enterprise.context.Initialized; import javax.enterprise.event.Observes; -import javax.enterprise.inject.Default; import javax.enterprise.inject.spi.*; -import javax.inject.Singleton; +import javax.inject.Named; import java.lang.invoke.MethodHandles; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.*; -import java.util.function.Function; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Supplier; - -import static org.axonframework.extensions.cdi.util.CdiUtilities.*; -import static org.axonframework.extensions.cdi.util.StringUtilities.lowerCaseFirstLetter; - +import java.util.stream.Collectors; /** - * @author Bert Laverman + * Main CDI extension class responsible for collecting CDI beans and setting up + * Axon configuration. */ -@Singleton public class AxonCdiExtension implements Extension { private static final Logger logger = LoggerFactory.getLogger( @@ -55,212 +57,418 @@ public class AxonCdiExtension implements Extension { private final Map> snapshotTriggerDefinitionProducerMap = new HashMap<>(); private final Map> commandTargetResolverProducerMap = new HashMap<>(); + private final List sagas = new ArrayList<>(); + private final Map> sagaStoreProducerMap = new HashMap<>(); + private final Map>> sagaConfigurationProducerMap = new HashMap<>(); + private final List messageHandlers = new ArrayList<>(); + private Producer eventStorageEngineProducer; + private Producer serializerProducer; + private Producer eventSerializerProducer; + private Producer messageSerializerProducer; + private Producer eventBusProducer; + private Producer commandBusProducer; +// private Producer commandGatewayProducer; + private Producer configurerProducer; + private Producer transactionManagerProducer; + private Producer entityManagerProviderProducer; + private Producer tokenStoreProducer; + private Producer listenerInvocationErrorHandlerProducer; + private Producer errorHandlerProducer; + private final List> correlationDataProviderProducers = new ArrayList<>(); + private Producer queryBusProducer; + private Producer queryGatewayProducer; + private Producer queryUpdateEmitterProducer; + private final List> moduleConfigurationProducers = new ArrayList<>(); + private final List> configurerModuleProducers = new ArrayList<>(); + private final List> eventUpcasterProducers = new ArrayList<>(); + private Producer deadlineManagerProducer; + private Producer eventProcessingModuleProducer; + private Producer eventProcessingConfigurationProducer; + private Producer resourceInjectorProducer; + + // Mark: Many of the beans and producers I am processing may use + // container resources such as entity managers, etc. I believe this means + // I should be handling them as late as possible to avoid initialization + // timing issues. Right now things are processed as they make + // "semantic" sense. Do you think this could be improved to do the same + // processing later? + //X @struberg: it will work. But probably might be better to observe ProcessBeanAttributes pba and use pba.getAnnotated + //X that way you will also have the ability to pick up modifications of those classes via ProcessAnnotatedType. + //X Not sure if this is really needed for your users though. /** - * Produce a bean, using the given {@link BeanManager} and {@link Producer}. + * Scans all annotated types with the {@link Aggregate} annotation and + * collects them for registration. * - * @param beanManager the CDI BeanManager to provide the context. - * @param producer the (registered) producer for this bean class. - * @param The class of the bean. - * @return the requested instance. + * @param processAnnotatedType annotated type processing event. */ - private T produce(BeanManager beanManager, Producer producer) { - return producer.produce(beanManager.createCreationalContext(null)); + // Mark: All I need to do here is look up what the aggregate classes are + // and what the value of the @Aggregate annotation is. This feels a little + // overkill. That said, currently I do need these values in afterBeanDiscovery + // and this might be the most efficient way of collecting these anyway. + // Other than being a bit ugly, this is not anything that is causing any + // functional issues. + void processAggregate(@Observes @WithAnnotations({Aggregate.class}) + final ProcessAnnotatedType processAnnotatedType) { + // TODO Aggregate classes may need to be vetoed so that CDI does not + // actually try to manage them. + + final Class clazz = processAnnotatedType.getAnnotatedType().getJavaClass(); + + logger.debug("Found aggregate: {}.", clazz); + + aggregates.add(new AggregateDefinition(clazz)); } - private Map, AnnotatedType> defaultBeans = new HashMap<>(); - private Map> defaultProducers = new HashMap<>(); - private Map> defaultProducerBeanClasses = new HashMap<>(); - private Set nonDefaultProducers = new HashSet<>(); + // Mark: Same issues with the stuff below. A bit ugly but functional. I may + // be able to get rid of most fo this by doing BeanManager look ups right + // after afterBeanDiscovery or later. + void processAggregateRepositoryProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Found producer for repository: {}.", processProducer.getProducer()); + + String repositoryName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + + this.aggregateRepositoryProducerMap.put(repositoryName, processProducer.getProducer()); + } + + void processSnapshotTriggerDefinitionProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Found producer for snapshot trigger definition: {}.", + processProducer.getProducer()); + + String triggerDefinitionName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + + this.snapshotTriggerDefinitionProducerMap.put(triggerDefinitionName, processProducer.getProducer()); + } + + void processCommandTargetResolverProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Found producer for command target resolver: {}.", processProducer.getProducer()); + + String resolverName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + + this.commandTargetResolverProducerMap.put(resolverName, processProducer.getProducer()); + } + + void processSaga(@Observes @WithAnnotations({Saga.class}) + final ProcessAnnotatedType processAnnotatedType) { + // TODO Saga classes may need to be vetoed so that CDI does not + // actually try to manage them. + + final Class clazz = processAnnotatedType.getAnnotatedType().getJavaClass(); + + logger.debug("Found saga: {}.", clazz); + + sagas.add(new SagaDefinition(clazz)); + } /** - * Collect bean classes marked with the {code @IfNoneDefined} annotation. + * Scans for an event storage engine producer. * - * @param pat the {@link ProcessAnnotatedType} event. - * @param the class of the bean. + * @param processProducer process producer event. */ - void processDefaultBeans(@Observes final ProcessAnnotatedType pat) { - AnnotatedType at = pat.getAnnotatedType(); - IfNoneDefined noneDefined = at.getAnnotation(IfNoneDefined.class); - - if (noneDefined != null) { - logger.info("processAnnotatedType(): Found default bean. Vetoing it for now."); - defaultBeans.put(noneDefined.value(), at); - pat.veto(); - } + // Mark: I know these seem especially frivolous and looks like they may be + // replaced with post deployment look-ups or injection of some kind. + // Unfortunately I don't think it is that straightforwards for reasons + // explained a bit later. That said, I do want to discuss these with you. + void processEventStorageEngineProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for event storage engine found: {}.", + processProducer.getProducer()); + + this.eventStorageEngineProducer = processProducer.getProducer(); + } + + void processSagaConfigurationProducer( + @Observes final ProcessProducer> processProducer) { + logger.debug("Producer for saga configuration found: {}.", + processProducer.getProducer()); + + String sagaConfigurationName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + + sagaConfigurationProducerMap.put(sagaConfigurationName, processProducer.getProducer()); } /** - * Collect the producers marked with the {@code @IfNoneDefined} qualifier. + * Scans for a configurer producer. * - * @param processProducer the {@link ProcessProducer} event. - * @param the bean class of the bean that declares the producer. - * @param the return type of the producer. + * @param processProducer process producer event. */ - void processDefaultProducers(@Observes final ProcessProducer processProducer) { - AnnotatedMember member = processProducer.getAnnotatedMember(); - if (member instanceof AnnotatedMethod) { - final AnnotatedMethod am = (AnnotatedMethod)member; - final Type returnType = am.getBaseType(); - - final IfNoneDefined noneDefined = am.getAnnotation(IfNoneDefined.class); - - if (noneDefined != null) { - logger.info("processAnnotatedType(): Found default producer for {}. \"{}::{}\".", - returnType, - am.getDeclaringType().getJavaClass().getName(), - am.getJavaMember().getName()); - defaultProducers.put(returnType, processProducer.getProducer()); - defaultProducerBeanClasses.put(returnType, am.getJavaMember().getReturnType()); - } - else { - nonDefaultProducers.add(returnType); - } - } -// else if (member instanceof AnnotatedField) { -// logger.info("processDefaultProducers(): Ignoring @Produces on a {}.", member.getClass().getName()); -// } -// else { -// logger.info("processDefaultProducers(): Ignoring @Produces on a {}.", member.getClass().getName()); -// } + void processConfigurerProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for configurer found: {}.", processProducer.getProducer()); + + this.configurerProducer = processProducer.getProducer(); } /** - * Add beans from all {@code @IfNoneDefined} marked producers, if none is already avilable. + * Scans for a transaction manager producer. * - * @param afterBeanDiscovery the {@link AfterBeanDiscovery} event. - * @param mgr the {@link BeanManager}. + * @param processProducer process producer event. */ - private void addMissingProducers(final AfterBeanDiscovery afterBeanDiscovery, final BeanManager mgr) { - logger.info("addMissingProducers(): Checking for missing producers."); - for (Type tp: defaultProducers.keySet()) { - if (!nonDefaultProducers.contains(tp)) { - if (defaultProducers.containsKey(tp)) { - final Class beanClass = defaultProducerBeanClasses.get(tp); - logger.info("addMissingProducers(): Enabling our default producer for \"{}\" (beanClass \"{}\".", tp, beanClass); - afterBeanDiscovery.addBean(new BeanBuilder<>(mgr) - .beanClass(beanClass) - .types(tp) - .scope(ApplicationScoped.class) - .qualifiers(AnnotationInstanceProvider.of(Default.class)) - .beanLifecycle(new SupplierBasedLifeCycle<>(() -> produce(mgr, defaultProducers.get(tp)))) - .create()); - } - else { - logger.warn("addMissingProducers(): We seem to lack a producer for \"{}\".", tp); - } - } - } - logger.info("addMissingProducers(): Done"); + void processTransactionManagerProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for transaction manager found: {}.", + processProducer.getProducer()); + + this.transactionManagerProducer = processProducer.getProducer(); } - private Bean defineBean(BeanManager beanManager, AnnotatedType annotatedType) { - BeanAttributes beanAttributes = beanManager.createBeanAttributes(annotatedType); - InjectionTargetFactory injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType); - return beanManager.createBean(beanAttributes, annotatedType.getJavaClass(), injectionTargetFactory); + /** + * Scans for a serializer producer. + * + * @param processProducer process producer event. + */ + void processSerializerProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple serializer definitions of the same type. + + AnnotatedMember annotatedMember = processProducer.getAnnotatedMember(); + Named named = annotatedMember.getAnnotation(Named.class); + + if (named != null) { + String namedValue = named.value(); + String serializerName = "".equals(namedValue) + ? annotatedMember.getJavaMember().getName() + : namedValue; + switch (serializerName) { + case "eventSerializer": + logger.debug("Producer for event serializer found: {}.", + processProducer.getProducer()); + eventSerializerProducer = processProducer.getProducer(); + break; + case "messageSerializer": + logger.debug("Producer for message serializer found: {}.", + processProducer.getProducer()); + messageSerializerProducer = processProducer.getProducer(); + break; + case "serializer": + logger.debug("Producer for serializer found: {}.", + processProducer.getProducer()); + this.serializerProducer = processProducer.getProducer(); + break; + default: + logger.warn("Unknown named serializer configured: " + serializerName); + } + } else { + logger.debug("Producer for serializer found: {}.", processProducer.getProducer()); + this.serializerProducer = processProducer.getProducer(); + } } - private void addMissingBeans(final AfterBeanDiscovery afterBeanDiscovery, final BeanManager mgr) { - logger.info("addMissingBeans(): Checking for missing beans."); + /** + * Scans for an event bus producer. + * + * @param processProducer process producer event. + */ + void processEventBusProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. - defaultBeans.entrySet().stream() - .filter(e -> mgr.getBeans(e.getKey()).isEmpty()) - .map(e -> defineBean(mgr, e.getValue())) - .forEach(afterBeanDiscovery::addBean); + logger.debug("Producer for event bus found: {}.", processProducer.getProducer()); - logger.info("addMissingBeans(): Done"); + this.eventBusProducer = processProducer.getProducer(); } /** - * Collect all classes annotated with {@link Aggregate}. + * Scans for a command bus producer. * - * @param processAnnotatedType the CDI wrapper for the class. - * @param the class itself. + * @param processProducer process producer event. */ - void processAggregate(@Observes @WithAnnotations({Aggregate.class}) - final ProcessAnnotatedType processAnnotatedType) { - // TODO Aggregate classes may need to be vetoed so that CDI does not - // actually try to manage them. + void processCommandBusProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. - final Class clazz = processAnnotatedType.getAnnotatedType().getJavaClass(); - - logger.info("Found aggregate: {}.", clazz); + logger.debug("Producer for command bus found: {}.", + processProducer.getProducer()); - aggregates.add(new AggregateDefinition(clazz)); + this.commandBusProducer = processProducer.getProducer(); } +// void processCommandGatewayProducer( +// @Observes final ProcessProducer processProducer) { +// // TODO Handle multiple producer definitions. +// +// logger.debug("Producer for command gateway found: {}.", +// processProducer.getProducer()); +// +// this.commandGatewayProducer = processProducer.getProducer(); +// } + /** - * Collect {@link Repository} producers. + * Scans for an entity manager provider producer. * - * @param processProducer the CDI wrapper for the producer. - * @param the producer's class. + * @param processProducer process producer event. */ - void processAggregateRepositoryProducer( - @Observes final ProcessProducer processProducer) { - logger.info("Found producer for repository: {}.", processProducer.getProducer()); - - AnnotatedMember member = processProducer.getAnnotatedMember(); - if (member instanceof AnnotatedMethod) { - logger.info("processAggregateRepositoryProducer(): Checking @Produces on a method."); - AnnotatedMethod am = (AnnotatedMethod)member; - final Type type = am.getBaseType(); - logger.info("processAggregateRepositoryProducer(): The baseType is {}. ({})", type.toString(), type.getClass().getName()); - if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - logger.info("processAggregateRepositoryProducer(): It is a ParameterizedType."); - for (Type tp: pType.getActualTypeArguments()) { - logger.info("processAggregateRepositoryProducer(): - {}", tp.toString()); - } - } - } - else if (member instanceof AnnotatedField) { - logger.info("processAggregateRepositoryProducer(): Ignoring @Produces on a {}.", member.getClass().getName()); - } - else { - logger.info("processAggregateRepositoryProducer(): Ignoring @Produces on a {}.", member.getClass().getName()); - } - String repositoryName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + void processEntityManagerProviderProducer( + @Observes final ProcessProducer processProducer) { + // TODO Investigate if there is a way to look up the entity manager + // from the environment. There likely isn't. + // TODO Handle multiple producer definitions. - this.aggregateRepositoryProducerMap.put(repositoryName, processProducer.getProducer()); + logger.debug("Producer for entity manager provider found: {}.", + processProducer.getProducer()); + + this.entityManagerProviderProducer = processProducer.getProducer(); } /** - * Collect {@link SnapshotTriggerDefinition} producers. + * Scans for a token store producer. * - * @param processProducer the CDI wrapper for the producer. - * @param the producer's class. + * @param processProducer process producer event. */ - void processSnapshotTriggerDefinitionProducer( - @Observes final ProcessProducer processProducer) { - logger.info("Found producer for snapshot trigger definition: {}.", + void processTokenStoreProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for token store found: {}.", processProducer.getProducer()); + + this.tokenStoreProducer = processProducer.getProducer(); + } + + void processErrorHandlerProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for error handler found: {}.", processProducer.getProducer()); + + this.errorHandlerProducer = processProducer.getProducer(); + } + + void processListenerInvocationErrorHandlerProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for listener invocation error handler found: {}.", processProducer.getProducer()); - String triggerDefinitionName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + this.listenerInvocationErrorHandlerProducer = processProducer.getProducer(); + } - this.snapshotTriggerDefinitionProducerMap.put(triggerDefinitionName, processProducer.getProducer()); + void processCorrelationDataProviderProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Producer for correlation data provider found: {}.", + processProducer.getProducer()); + + this.correlationDataProviderProducers.add(processProducer.getProducer()); } - /** - * Collect {@link CommandTargetResolver} producers. - * - * @param processProducer the CDI wrapper for the producer. - * @param the producer's class. - */ - void processCommandTargetResolverProducer( - @Observes final ProcessProducer processProducer) { - logger.info("Found producer for command target resolver: {}.", processProducer.getProducer()); + void processQueryBusProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. - String resolverName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + logger.debug("Producer for query bus found: {}.", + processProducer.getProducer()); - this.commandTargetResolverProducerMap.put(resolverName, processProducer.getProducer()); + this.queryBusProducer = processProducer.getProducer(); + } + + void processQueryGatewayProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for query gateway found: {}.", + processProducer.getProducer()); + + this.queryGatewayProducer = processProducer.getProducer(); + } + + void processQueryUpdateEmitterProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for query update emitter found: {}.", processProducer.getProducer()); + + this.queryUpdateEmitterProducer = processProducer.getProducer(); + } + + void processDeadlineManagerProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for deadline manager found: {}.", processProducer.getProducer()); + + this.deadlineManagerProducer = processProducer.getProducer(); + } + + void processModuleConfigurationProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Producer for module configuration found: {}.", + processProducer.getProducer()); + + this.moduleConfigurationProducers.add(processProducer.getProducer()); + } + + void processEventHandlingConfigurationProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for event handling configuration found: {}.", + processProducer.getProducer()); + + this.eventProcessingModuleProducer = processProducer.getProducer(); + } + + void processEventProcessingConfigurationProducer( + @Observes final ProcessProducer processProducer) { + // TODO Handle multiple producer definitions. + + logger.debug("Producer for event processing configuration found: {}.", + processProducer.getProducer()); + + this.eventProcessingConfigurationProducer = processProducer.getProducer(); + } + + void processConfigurerModuleProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Producer for configurer module found: {}.", processProducer.getProducer()); + + this.configurerModuleProducers.add(processProducer.getProducer()); + } + + void processSagaStoreProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Producer for saga store found: {}.", processProducer.getProducer()); + + String sagaStoreName = CdiUtilities.extractBeanName(processProducer.getAnnotatedMember()); + + this.sagaStoreProducerMap.put(sagaStoreName, processProducer.getProducer()); + } + + void processEventUpcasterProducer( + @Observes final ProcessProducer processProducer) { + logger.debug("Producer for event upcaster found: {}.", processProducer.getProducer()); + + this.eventUpcasterProducers.add(processProducer.getProducer()); + } + + void resourceInjectorProducer( + @Observes final ProcessProducer processProducer) { + + logger.debug("Producer for resource injector found: {}.", processProducer.getProducer()); + + this.resourceInjectorProducer = processProducer.getProducer(); } /** - * Collect message handlers for later processing. - * @param processBean the CDI wrapper for the bean. - * @param the class of the bean. + * Scans all beans and collects beans with message handlers. + * + * @param processBean bean processing event. */ + // Mark: This one is especally tricky. I am looking for the existance + // of annotations and methods and collecting bean definitions. + // I suspect this is the most efficient way to do this and I can use + // the bean defenitions to look up via BeanManager later. + //X @struberg it's fine. Although I would use processBean.getAnnotated() instead of bean.getBeanClass() in the 'inspect' code + //X I'fe quickly fixed this method. Please verify if it works as expected. And you still need to change the tests to AnnotatedType! void processBean(@Observes final ProcessBean processBean) { MessageHandlingBeanDefinition.inspect(processBean.getBean(), processBean.getAnnotated()) .ifPresent(bean -> { @@ -269,268 +477,473 @@ void processBean(@Observes final ProcessBean processBean) { }); } - @SuppressWarnings("unchecked") - private void registerAggregates(final AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager, Configurer configurer) { - aggregates.forEach(aggregateDefinition -> { - logger.info("registerAggregates(): Registering aggregate: {}.", aggregateDefinition.aggregateType().getSimpleName()); + /** + * Registration of Axon components in CDI registry. + */ + void afterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery, + final BeanManager beanManager) { + logger.info("Starting Axon Framework configuration."); + + Configurer configurer; + + if (this.configurerProducer != null) { + // Mark: this is one of the biggest headaches I am facing. What I + // need is the actual bean instance that I need to move forward with. + // Right now what I am doing is just having the producer create it. + // The problem with this is that CDI and the Java EE runtime is just + // not fullly ready to do that at this stage. + + //X @struberg: don't understand why exactly. Miss the context. + //X I fear I'd need a way better understanding of Axon + + // In addition, the + // bean may be referencing definitions I have not had a chance + // to register with CDI yet since they can only be produced after the + // Axon configuration is done. + // + // The only solution I can think of is register any bean + // definitions I've detected I absolutely need and avoid actual + // bean reference until later, create the beans like configurer + // that I say in the API cannot have circular dependencies and move the + // configuration steps itself to be as lazily loaded as possible. + // Can you think of a better strategy to deal with this problem? + configurer = produce(beanManager, configurerProducer); + logger.info("Starting with an application provided configurer: {}.", + configurer.getClass().getSimpleName()); + } else { + logger.info("Starting with the Axon default configuration."); + + configurer = DefaultConfigurer.defaultConfiguration(); + } - AggregateConfigurer aggregateConfigurer - = AggregateConfigurer.defaultConfiguration(aggregateDefinition.aggregateType()); + if (this.entityManagerProviderProducer != null) { + final EntityManagerProvider entityManagerProvider + = produce(beanManager, entityManagerProviderProducer); - if (aggregateDefinition.repository().isPresent()) { + logger.info("Registering entity manager provider: {}.", + entityManagerProvider.getClass().getSimpleName()); - final String repoBeanName = aggregateDefinition.repository().get(); - logger.info("registerAggregates(): This aggregate has a repository specified by name (\"{}\").", repoBeanName); + configurer.registerComponent(EntityManagerProvider.class, c -> entityManagerProvider); + } - aggregateConfigurer.configureRepository(c -> produce(beanManager, aggregateRepositoryProducerMap - .get(repoBeanName))); - } else { - logger.info("registerAggregates(): This aggregate has a no repository specified."); + if (this.serializerProducer != null) { + final Serializer serializer = produce(beanManager, serializerProducer); - final String repoName = aggregateDefinition.repositoryName(); - if (aggregateRepositoryProducerMap.containsKey(repoName)) { - logger.info("registerAggregates(): We have a producer for \"{}\".", repoName); + logger.info("Registering serializer: {}.", serializer.getClass().getSimpleName()); - aggregateConfigurer.configureRepository( - c -> produce(beanManager, aggregateRepositoryProducerMap.get(repoName))); - } else { - logger.info("registerAggregates(): This aggregate has a no repository and no producer specified."); - - String repositoryName = lowerCaseFirstLetter(aggregateDefinition.aggregateType().getSimpleName()) + "Repository"; - String factoryName = lowerCaseFirstLetter(aggregateDefinition.aggregateType().getName()) + "AggregateFactory"; - logger.info("registerAggregates(): Repository beanName = \"{}\", factory name = \"{}\".", repoName, factoryName); - - Optional repo = getInstance(beanManager, repositoryName, Repository.class); - if (!repo.isPresent()) { - logger.info("registerAggregates(): We have no registered bean for \"{}\".", repositoryName); - - final ParameterizedType repoType = TypeUtils.parameterize(Repository.class, aggregateDefinition.aggregateType()); - logger.info("registerAggregates(): Adding a bean with type {}", repoType); - - afterBeanDiscovery.addBean(new BeanBuilder<>(beanManager) - .beanClass(Repository.class) - .types(repoType) - .qualifiers(AnnotationInstanceProvider.of(Default.class)) - .scope(ApplicationScoped.class) - .beanLifecycle(new SupplierBasedLifeCycle<>(() -> aggregateConfigurer.repository())) - .create()); - } - else { - logger.info("registerAggregates(): We have a registered bean named \"{}\".", repositoryName); - } + configurer.configureSerializer(c -> serializer); + } - // TODO: 8/29/2018 check how to do in CDI world: aggregate factory - aggregateDefinition.snapshotTriggerDefinition().ifPresent(triggerDefinition -> aggregateConfigurer - .configureSnapshotTrigger( - c -> produce(beanManager, snapshotTriggerDefinitionProducerMap - .get(triggerDefinition)))); - if (aggregateDefinition.isJpaAggregate()) { - aggregateConfigurer.configureRepository( - c -> (Repository) GenericJpaRepository.builder(aggregateDefinition.aggregateType()) - // TODO: 8/29/2018 what to do about default EntityManagerProvider (check spring impl) - .entityManagerProvider(c.getComponent(EntityManagerProvider.class)) - .eventBus(c.eventBus()) - .repositoryProvider(c::repository) - .lockFactory(c.getComponent(LockFactory.class, () -> NullLockFactory.INSTANCE)) - .parameterResolverFactory(c.parameterResolverFactory()) - .handlerDefinition(c.handlerDefinition(aggregateDefinition.aggregateType())) - .build()); - } - } - } + if (this.eventSerializerProducer != null) { + final Serializer serializer = produce(beanManager, eventSerializerProducer); - if (aggregateDefinition.commandTargetResolver().isPresent()) { - aggregateConfigurer.configureCommandTargetResolver( - c -> produce(beanManager, - commandTargetResolverProducerMap.get(aggregateDefinition.commandTargetResolver().get()))); - } else { - commandTargetResolverProducerMap.keySet() - .stream() - .filter(resolver -> aggregates.stream() - .filter(a -> a.commandTargetResolver().isPresent()) - .map(a -> a.commandTargetResolver().get()) - .noneMatch(resolver::equals)) - .findFirst() // TODO: 8/29/2018 what if there are more "default" resolvers - .ifPresent(resolver -> aggregateConfigurer.configureCommandTargetResolver( - c -> produce(beanManager, - commandTargetResolverProducerMap.get(resolver)))); - } + logger.info("Registering event serializer: {}.", serializer.getClass().getSimpleName()); - configurer.configureAggregate(aggregateConfigurer); - }); - } + configurer.configureEventSerializer(c -> serializer); + } - private void registerMessageHandlers(final BeanManager beanManager, final Configurer configurer) { - final EventProcessingConfigurer eventProcessingConfigurer = configurer.eventProcessing(); + if (this.messageSerializerProducer != null) { + final Serializer serializer = produce(beanManager, messageSerializerProducer); - messageHandlers.stream().filter(MessageHandlingBeanDefinition::isMessageHandler).forEach(handler -> { - switch (handler.getScope()) { - case SINGLETON: - case APPLICATION_SCOPED: { - final Function handlerBuilder = c -> getInstance(handler.getBean().getBeanClass()); - if (handler.isEventSourcingHandler()) { - logger.warn("registerMessageHandlers(): Odd, an @EventSourcingHandler that is not an Aggregate? Ignoring for now."); - } - else if (handler.isEventHandler()) { - logger.info("registerMessageHandlers(): Registering \"{}\" as event handler.", handler.getBean().getBeanClass().getName()); + logger.info("Registering message serializer: {}.", serializer.getClass().getSimpleName()); - eventProcessingConfigurer.registerEventHandler(handlerBuilder); - } - if (handler.isCommandHandler()) { - logger.info("registerMessageHandlers(): Registering \"{}\" as command handler.", handler.getBean().getBeanClass().getName()); + configurer.configureMessageSerializer(c -> serializer); + } - configurer.registerCommandHandler(handlerBuilder); - } - if (handler.isQueryHandler()) { - logger.info("registerMessageHandlers(): Registering \"{}\" as query handler.", handler.getBean().getBeanClass().getName()); + if (this.transactionManagerProducer != null) { + final TransactionManager transactionManager + = produce(beanManager, transactionManagerProducer); - configurer.registerQueryHandler(handlerBuilder); - } - break; - } - case SESSION_SCOPED: - case CONVERSATION_SCOPED: - case REQUEST_SCOPED: - case DEPENDENT: - case UNKNOWN: { - logger.error("registerMessageHandlers(): Only dealing with @ApplicationScoped and @Singleton for now. Ignoring \"{}\".", handler.getBean().getBeanClass().getName()); - } - } - }); - } + logger.info("Registering transaction manager: {}.", transactionManager.getClass().getSimpleName()); - private void registerModules(final BeanManager mgr, Configurer configurer) { - registerConfigurerModules(mgr, configurer); - registerModuleConfigurations(mgr, configurer); - } + configurer.configureTransactionManager(c -> transactionManager); + } + + if (this.commandBusProducer != null) { + final CommandBus commandBus = produce(beanManager, commandBusProducer); + + logger.info("Registering command bus: {}.", commandBus.getClass().getSimpleName()); + + configurer.configureCommandBus(c -> commandBus); + } + + // Module configurations registration. + // TODO: 8/30/2018 : + // There is a possible issue with following construct: + // @Produce + // public ModuleConfiguration eventHandlingConfiguration() { + // return new EventHandlingConfiguration(); + // } + // It will be registered as module configuration only and not as EventHandlingConfiguration! + // This will result in having double EventHandlingConfiguration within Configuration. + // The same applies to EventProcessingConfiguration. + // Milan: I need to understand this a bit more from you, but I think the solution + // is to simply mandate the presence of @Named for EventHandlingConfiguration + // and EventProcessingConfiguration that is produced as type ModuleConfiguration + // but is not really a module configuration. + for (Producer producer : moduleConfigurationProducers) { + // Milan: This looks very interesting. I would like to understand why + // we are doing this and whether we should do this more globally, + // perhaps moving the entire Axon configuration to AfterDeploymentValidation + // instead of AfterBeanDiscovery. + // Antoine: Do you have any thoughts on this? Could this approach + // help solve any potential initialization issues? What's the easiest + // way to do this if so? + configurer.registerModule(new LazyRetrievedModuleConfiguration(() -> { + ModuleConfiguration moduleConfiguration + = producer.produce(beanManager.createCreationalContext(null)); + logger.info("Registering module configuration: {}.", + moduleConfiguration.getClass().getSimpleName()); + return moduleConfiguration; + })); + } + + if (eventProcessingModuleProducer != null) { + EventProcessingModule mod = produce(beanManager, eventProcessingModuleProducer); + + logger.info("Registering event processing configuration: {}.", mod.getClass().getSimpleName()); + + configurer.registerModule(mod); + } + + configurerModuleProducers.forEach(producer -> { + ConfigurerModule configurerModule = produce(beanManager, producer); + + logger.info("Configuring module: {}.", configurerModule.getClass().getSimpleName()); - private void registerConfigurerModules(final BeanManager mgr, Configurer configurer) { - Set> configurerModules = mgr.getBeans(ConfigurerModule.class); - for (Bean configurerModuleBean : configurerModules) { - ConfigurerModule configurerModule = (ConfigurerModule) configurerModuleBean.create(mgr.createCreationalContext(null)); configurerModule.configureModule(configurer); + }); + + if (this.eventBusProducer != null) { + final EventBus eventBus = produce(beanManager, eventBusProducer); + + logger.info("Registering event bus: {}.", eventBus.getClass().getSimpleName()); + + configurer.configureEventBus(c -> eventBus); } - } - private void registerModuleConfigurations(final BeanManager mgr, Configurer configurer) { - Set> moduleConfigurations = mgr.getBeans(ModuleConfiguration.class); - for (Bean moduleConfigurationBean : moduleConfigurations) { - configurer.registerModule(new LazyRetrievedModuleConfiguration( - () -> (ModuleConfiguration) moduleConfigurationBean.create(mgr.createCreationalContext(null)), - moduleConfigurationBean.getBeanClass()) - ); + if (this.tokenStoreProducer != null) { + final TokenStore tokenStore = produce(beanManager, tokenStoreProducer); + + logger.info("Registering token store: {}.", tokenStore.getClass().getSimpleName()); + + configurer.registerComponent(TokenStore.class, c -> tokenStore); } - } - private Configuration buildConfiguration(Configurer configurer) { - logger.info("Starting Axon configuration."); + if (this.listenerInvocationErrorHandlerProducer != null) { + ListenerInvocationErrorHandler listenerInvocationErrorHandler + = produce(beanManager, listenerInvocationErrorHandlerProducer); - return configurer.buildConfiguration(); - } + logger.info("Registering listener invocation error handler: {}.", + listenerInvocationErrorHandler.getClass().getSimpleName()); - private void afterTypeDiscovery(@Observes final AfterTypeDiscovery afterTypeDiscovery) { - logger.info("******* AfterTypeDiscovery"); - } + configurer.registerComponent(ListenerInvocationErrorHandler.class, + c -> listenerInvocationErrorHandler); + } - private void beforeBeanDiscovery(@Observes final BeforeBeanDiscovery beforeBeanDiscovery) { - logger.info("******* BeforeBeanDiscovery"); - } + if (this.errorHandlerProducer != null) { + ErrorHandler errorHandler = produce(beanManager, errorHandlerProducer); - private void afterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery, - final BeanManager beanManager) { - logger.info("******* AfterBeanDiscovery"); + logger.info("Registering error handler: {}.", errorHandler.getClass().getSimpleName()); - final Configurer configurer = ConfigurerProducer.instance(); + configurer.registerComponent(ErrorHandler.class, c -> errorHandler); + } - registerModules(beanManager, configurer); + // Correlation data providers registration + List correlationDataProviders = this.correlationDataProviderProducers + .stream() + .map(producer -> { + CorrelationDataProvider correlationDataProvider = produce(beanManager, producer); + + logger.info("Registering correlation data provider: {}.", + correlationDataProvider.getClass().getSimpleName()); + + return correlationDataProvider; + }) + .collect(Collectors.toList()); + configurer.configureCorrelationDataProviders(c -> correlationDataProviders); + + this.eventUpcasterProducers + .forEach(producer -> { + EventUpcaster eventUpcaster = produce(beanManager, producer); + logger.info("Registering event upcaster: {}.", + eventUpcaster.getClass().getSimpleName()); + configurer.registerEventUpcaster(c -> eventUpcaster); + }); - registerAggregates(afterBeanDiscovery, beanManager, configurer); - registerMessageHandlers(beanManager, configurer); + if (this.queryBusProducer != null) { + QueryBus queryBus = produce(beanManager, queryBusProducer); + + logger.info("Registering query bus: {}.", queryBus.getClass().getSimpleName()); + + configurer.configureQueryBus(c -> queryBus); + } - addMissingProducers(afterBeanDiscovery, beanManager); - addMissingBeans(afterBeanDiscovery, beanManager); + if (this.queryGatewayProducer != null) { + QueryGateway queryGateway = produce(beanManager, queryGatewayProducer); - resolveBean(beanManager, EventStorageEngine.class) - .ifPresent(bean -> configurer.configureEmbeddedEventStore(c -> getBean(beanManager, bean, EventStorageEngine.class).get())); - resolveBean(beanManager, TransactionManager.class) - .ifPresent(bean -> configurer.configureTransactionManager(c -> getBean(beanManager, bean, TransactionManager.class).get())); - resolveBean(beanManager, EventBus.class) - .ifPresent(bean -> configurer.configureEventBus(c -> getBean(beanManager, bean, EventBus.class).get())); + logger.info("Registering query gateway: {}.", queryGateway.getClass().getSimpleName()); - afterBeanDiscovery.addBean(new BeanBuilder<>(beanManager) - .beanClass(Configuration.class) - .types(Configuration.class) - .qualifiers(AnnotationInstanceProvider.of(Default.class)) - .scope(ApplicationScoped.class) - .beanLifecycle(new SupplierBasedLifeCycle<>(() -> buildConfiguration(configurer))) - .create()); + configurer.registerComponent(QueryGateway.class, c -> queryGateway); + } + + if (this.queryUpdateEmitterProducer != null) { + QueryUpdateEmitter queryUpdateEmitter = produce(beanManager, queryUpdateEmitterProducer); + + logger.info("Registering query update emitter: {}.", queryUpdateEmitter.getClass().getSimpleName()); + + configurer.configureQueryUpdateEmitter(c -> queryUpdateEmitter); + } + + if (this.deadlineManagerProducer != null) { + DeadlineManager deadlineManager = produce(beanManager, deadlineManagerProducer); + + logger.info("Registering deadline manager: {}.", deadlineManager.getClass().getSimpleName()); + + configurer.registerComponent(DeadlineManager.class, c -> deadlineManager); + } + + if (this.eventStorageEngineProducer != null) { + final EventStorageEngine eventStorageEngine = produce(beanManager, eventStorageEngineProducer); + + logger.info("Registering event storage: {}.", eventStorageEngine.getClass().getSimpleName()); + + configurer.configureEmbeddedEventStore(c -> eventStorageEngine); + } + + if (this.resourceInjectorProducer != null) { + ResourceInjector resourceInjector = produce(beanManager, resourceInjectorProducer); + + logger.info("Registering resource injector: {}.", resourceInjector.getClass().getSimpleName()); + + configurer.registerComponent(ResourceInjector.class, c -> resourceInjector); + } + + // Now need to begin registering application components rather than + // configuration components. + registerAggregates(beanManager, configurer); + registerSagaStore(beanManager, configurer); + registerSagas(beanManager, afterBeanDiscovery, configurer); + registerMessageHandlers(beanManager, configurer); + + logger.info("Axon Framework configuration complete."); + + logger.info("Registering Axon APIs with CDI."); + + + // Mark: This is one of the key parts that is forcing me to do anything at all + // in the afterBeanDiscovery phase. Once the Axon configuration completes, + // including taking into account user defined overrides, I need to bind some + // bean definitions with CDI. Aside from these relatively static definitions, + // there are and will be loops where I need to register things that look like + // Repository...Repository. I think this means + // I absolutely need to make sure of and thus finish my Axon configuration somehow very + // close to afterBeanDiscovery, in a programmatic fashion. + + //X @struberg: do you know that you can send CDI events between CDI Extensions already during bootstrap? + //X That might help here. + //X So you effectively can send out a ConfigurationEvent and anybody could write an own CDI Extension where + //X this gets observed. That way you can effectively have configuration and inter-CDI communication already before + //X the CDI container is booted. + //X I think I need a better explanation what problem you want to solve by it though. + + // + // That said, you'll notice the lambdas below that are my effort to defer + // actual instantiation to as late as possible. The only other way I can + // think of to defer actual bean referencing any further is by using + // byte code proxies that look up bean references on each method call. + // Can you think of a better way to further defer the actual configution start and + // bean referencing? + // + // Even with this deferring, I am running into issues like EJBs not + // instantiated and JPA registries not done yet on certain containers like + // Payara. When I refer to application scoped beans at this stage, it seems I am gettting + // my own copy and then the application creates another copy when the + // application code actually bootstraps. All related to referncing beans too + // early I believe. + + afterBeanDiscovery.addBean( + new BeanWrapper<>(Configuration.class, () -> startConfiguration(configurer))); + // Mark: I tried removing some of this code in favor of globally enabled alternatives. + // These are in AxonDefaultConfiguration. Howver, I immidiately ran into + // circular reference issues. I do not think alternatives are really + // a viable solution for this use case and I need to keep sticking to something + // similar to this unless you know of a way. + addIfNotConfigured(CommandBus.class, commandBusProducer, + () -> CdiUtilities.getReference(beanManager, Configuration.class).commandBus(), + afterBeanDiscovery); + addIfNotConfigured(QueryBus.class, queryBusProducer, + () -> CdiUtilities.getReference(beanManager, Configuration.class).queryBus(), + afterBeanDiscovery); + addIfNotConfigured(QueryGateway.class, queryGatewayProducer, + () -> CdiUtilities.getReference(beanManager, Configuration.class).queryGateway(), + afterBeanDiscovery); + addIfNotConfigured(QueryUpdateEmitter.class, + queryUpdateEmitterProducer, + () -> CdiUtilities.getReference(beanManager, Configuration.class).queryUpdateEmitter(), + afterBeanDiscovery); + addIfNotConfigured(EventBus.class, eventBusProducer, + () -> CdiUtilities.getReference(beanManager, Configuration.class).eventBus(), + afterBeanDiscovery); + addIfNotConfigured(Serializer.class, serializerProducer, + () -> CdiUtilities.getReference(beanManager, Configuration.class).serializer(), + afterBeanDiscovery); } - private void afterDeploymentValidation( + void afterDeploymentValidation( @Observes final AfterDeploymentValidation afterDeploymentValidation, final BeanManager beanManager) { - logger.info("******* AfterDeploymentValidation"); + // Ensure the configuration is started. + CdiUtilities.getReference(beanManager, Configuration.class).commandBus(); } - private void init(@Observes @Initialized(ApplicationScoped.class) Object initialized) { - logger.info("init(): ApplicationScoped is @Initialized!"); + void init(@Observes @Initialized(ApplicationScoped.class) Object initialized) { } - private void destroy(@Observes @Destroyed(ApplicationScoped.class) final Object destroyed) { - logger.info("init(): ApplicationScoped is @Destroyed!"); + void destroy(@Observes @Destroyed(ApplicationScoped.class) final Object destroyed) { } - private void beforeShutdown(@Observes BeforeShutdown event, BeanManager beanManager) { - logger.info("beforeShutdown()"); + void beforeShutdown(@Observes BeforeShutdown event, BeanManager beanManager) { } - private static class LazyRetrievedModuleConfiguration implements ModuleConfiguration { + private Configuration startConfiguration(Configurer configurer) { + logger.info("Starting Axon configuration."); - private final Supplier delegateSupplier; - private final Class moduleType; - private ModuleConfiguration delegate; + return configurer.start(); + } - LazyRetrievedModuleConfiguration(Supplier delegateSupplier, Class moduleType) { - this.delegateSupplier = delegateSupplier; - this.moduleType = moduleType; - } + private void registerMessageHandlers(BeanManager beanManager, Configurer configurer) { + for (MessageHandlingBeanDefinition messageHandler : messageHandlers) { + Component component = new Component<>(() -> null, "messageHandler", + c -> messageHandler.getBean().create(beanManager.createCreationalContext(null))); - @Override - public void initialize(Configuration config) { - getDelegate().initialize(config); - } + if (messageHandler.isEventHandler()) { + logger.info("Registering event handler: {}.", + messageHandler.getBean().getBeanClass().getSimpleName()); + configurer.eventProcessing(c -> c.registerEventHandler( conf ->component.get())); + } - @Override - public void start() { - getDelegate().start(); - } + if (messageHandler.isCommandHandler()) { + logger.info("Registering command handler: {}.", + messageHandler.getBean().getBeanClass().getSimpleName()); + configurer.registerCommandHandler(c -> component.get()); + } - @Override - public void shutdown() { - getDelegate().shutdown(); + if (messageHandler.isQueryHandler()) { + logger.info("Registering query handler: {}.", + messageHandler.getBean().getBeanClass().getSimpleName()); + configurer.registerQueryHandler(c -> component.get()); + } } + } - @Override - public int phase() { - return getDelegate().phase(); - } + @SuppressWarnings("unchecked") + private void registerAggregates(BeanManager beanManager, Configurer configurer) { + aggregates.forEach(aggregateDefinition -> { + logger.info("Registering aggregate: {}.", aggregateDefinition.aggregateType().getSimpleName()); - @Override - public ModuleConfiguration unwrap() { - return getDelegate(); - } + AggregateConfigurer aggregateConfigurer + = AggregateConfigurer.defaultConfiguration(aggregateDefinition.aggregateType()); - @Override - public boolean isType(Class type) { - return type.isAssignableFrom(moduleType); - } + if (aggregateDefinition.repository().isPresent()) { + aggregateConfigurer.configureRepository( + c -> produce(beanManager, aggregateRepositoryProducerMap + .get(aggregateDefinition.repository().get()))); + } else { + if (aggregateRepositoryProducerMap.containsKey(aggregateDefinition.repositoryName())) { + aggregateConfigurer.configureRepository( + c -> produce(beanManager, aggregateRepositoryProducerMap + .get(aggregateDefinition.repositoryName()))); + } else { + // TODO: 8/29/2018 check how to do in CDI world: register repository as a bean + // TODO: 8/29/2018 check how to do in CDI world: aggregate factory + aggregateDefinition.snapshotTriggerDefinition().ifPresent(triggerDefinition -> aggregateConfigurer + .configureSnapshotTrigger( + c -> produce(beanManager, snapshotTriggerDefinitionProducerMap + .get(triggerDefinition)))); + if (aggregateDefinition.isJpaAggregate()) { + aggregateConfigurer.configureRepository( + c -> (Repository)GenericJpaRepository.builder(aggregateDefinition.aggregateType()) + // TODO: 8/29/2018 what to do about default EntityManagerProvider (check spring impl) + .entityManagerProvider(c.getComponent(EntityManagerProvider.class)) + .eventBus(c.eventBus()) + .repositoryProvider(c::repository) + .lockFactory(c.getComponent(LockFactory.class, () -> NullLockFactory.INSTANCE)) + .parameterResolverFactory(c.parameterResolverFactory()) + .handlerDefinition(c.handlerDefinition(aggregateDefinition.aggregateType())) + .build()); + } + } + } + + if (aggregateDefinition.commandTargetResolver().isPresent()) { + aggregateConfigurer.configureCommandTargetResolver( + c -> produce(beanManager, + commandTargetResolverProducerMap.get(aggregateDefinition.commandTargetResolver().get()))); + } else { + commandTargetResolverProducerMap.keySet() + .stream() + .filter(resolver -> aggregates.stream() + .filter(a -> a.commandTargetResolver().isPresent()) + .map(a -> a.commandTargetResolver().get()) + .noneMatch(resolver::equals)) + .findFirst() // TODO: 8/29/2018 what if there are more "default" resolvers + .ifPresent(resolver -> aggregateConfigurer.configureCommandTargetResolver( + c -> produce(beanManager, + commandTargetResolverProducerMap.get(resolver)))); + } - private ModuleConfiguration getDelegate() { - if (delegate == null) { - delegate = delegateSupplier.get(); + configurer.configureAggregate(aggregateConfigurer); + }); + } + + private void registerSagaStore(BeanManager beanManager, Configurer configurer) { + sagaStoreProducerMap.keySet() + .stream() + .filter(storeName -> sagas.stream() + .filter(sd -> sd.sagaStore().isPresent()) + .map(sd -> sd.sagaStore().get()) + .noneMatch(storeName::equals)) + .findFirst() // TODO: 8/29/2018 what if there are more "default" saga stores??? + .ifPresent(storeName -> { + SagaStore sagaStore = produce(beanManager, sagaStoreProducerMap.get(storeName)); + logger.info("Registering saga store {}.", sagaStore.getClass().getSimpleName()); + configurer.registerComponent(SagaStore.class, c -> sagaStore); + }); + } + + @SuppressWarnings("unchecked") + private void registerSagas(BeanManager beanManager, AfterBeanDiscovery afterBeanDiscovery, Configurer configurer) { + sagas.forEach(sagaDefinition -> { + logger.info("Registering saga {}.", sagaDefinition.sagaType().getSimpleName()); + + if (!sagaDefinition.explicitConfiguration() + && !sagaConfigurationProducerMap.containsKey(sagaDefinition.configurationName())) { + + EventProcessingConfigurer cfgr = configurer.eventProcessing(); + + //TODO Make sure a SubscribingEventProcessor is used + cfgr.registerSaga(sagaDefinition.sagaType()); + final SagaConfigurer sagaConfigurer = SagaConfigurer.forType(sagaDefinition.sagaType()); + + afterBeanDiscovery.addBean(new BeanWrapper<>(sagaDefinition.configurationName(), + SagaConfigurer.class, () -> sagaConfigurer)); + sagaDefinition.sagaStore() + .ifPresent(sagaStore -> sagaConfigurer + .configureSagaStore(c -> produce(beanManager, sagaStoreProducerMap.get(sagaStore)))); } - return delegate; + }); + } + + private void addIfNotConfigured(Class componentType, Producer componentProducer, + Supplier componentSupplier, AfterBeanDiscovery afterBeanDiscovery) { + if (componentProducer == null) { + logger.info("Adding Bean for {}.", componentType.getName()); + afterBeanDiscovery.addBean(new BeanWrapper<>(componentType, componentSupplier)); } } + private T produce(BeanManager beanManager, Producer producer) { + return producer.produce(beanManager.createCreationalContext(null)); + } } diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/AxonDefaultConfiguration.java b/cdi/src/main/java/org/axonframework/extensions/cdi/AxonDefaultConfiguration.java new file mode 100644 index 0000000..0566eb9 --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/AxonDefaultConfiguration.java @@ -0,0 +1,82 @@ +package org.axonframework.extensions.cdi; + +import java.io.Serializable; +import java.lang.invoke.MethodHandles; +//import javax.annotation.Priority; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Alternative; +import javax.enterprise.inject.Produces; +import org.axonframework.commandhandling.gateway.CommandGateway; +import org.axonframework.config.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// Mark: I began this code in the hopes of moving most of the Axon +// configuration here to a more declarative style and also moving the bean +// instantiation to as late in the cycle as possible. The issue is that +// it results in a circular reference that I don't think can be resolved other +// than in a programmatic fashion in an extension. The circularity is +// demonstarted in the commented out code. Honestly, I really wanted this +// approach to work but I don't think it will unless you can see a way. +@ApplicationScoped +@Alternative +//@Priority(javax.interceptor.Interceptor.Priority.LIBRARY_BEFORE) +public class AxonDefaultConfiguration implements Serializable { + + private static final Logger logger = LoggerFactory.getLogger( + MethodHandles.lookup().lookupClass()); + +// @Produces +// @ApplicationScoped +// public Configuration configuration(Configurer configurer, Serializer serializer) { +// logger.info("Starting Axon Framework configuration."); +// +// configurer.configureSerializer(c -> serializer); +// +// logger.info("Starting Axon configuration."); +// +// return configurer.start(); +// } + + @Produces + @ApplicationScoped + public CommandGateway commandGateway(Configuration configuration) { + return configuration.commandGateway(); + } + +// @Produces +// @ApplicationScoped +// public CommandBus commandBus(Configuration configuration) { +// return configuration.commandBus(); +// } + +// @Produces +// @ApplicationScoped +// public QueryGateway queryGateway(Configuration configuration) { +// return configuration.queryGateway(); +// } + +// @Produces +// @ApplicationScoped +// public QueryBus queryBus(Configuration configuration) { +// return configuration.queryBus(); +// } + +// @Produces +// @ApplicationScoped +// public QueryUpdateEmitter queryUpdateEmitter(Configuration configuration) { +// return configuration.queryUpdateEmitter(); +// } + +// @Produces +// @ApplicationScoped +// public EventBus eventBus(Configuration configuration) { +// return configuration.eventBus(); +// } +// +// @Produces +// @ApplicationScoped +// public Serializer serializer(Configuration configuration) { +// return configuration.serializer(); +// } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/BeanScope.java b/cdi/src/main/java/org/axonframework/extensions/cdi/BeanScope.java deleted file mode 100644 index 0f6ed93..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/BeanScope.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.axonframework.extensions.cdi; - -import org.slf4j.Logger; - -import javax.enterprise.context.*; -import javax.inject.Singleton; - -import static java.lang.invoke.MethodHandles.lookup; -import static org.slf4j.LoggerFactory.getLogger; - -public enum BeanScope { - UNKNOWN(Class.class), - SINGLETON(Singleton.class), // There can be only one! - APPLICATION_SCOPED(ApplicationScoped.class), // The bean is expected to live until the application ends - SESSION_SCOPED(SessionScoped.class), // The bean is expected to live until the (web) session expires - CONVERSATION_SCOPED(ConversationScoped.class), // The bean is expected to live for the length of the current "conversation" - REQUEST_SCOPED(RequestScoped.class), // The bean is expected to live until the current (web) request has been replied to - DEPENDENT(Dependent.class); // There will be one bean for every context wherein the bean is requested - - private static final Logger logger = getLogger(lookup().lookupClass()); - - private Class annotation; - - BeanScope(Class annotation) { - this.annotation = annotation; - } - - public Class getAnnotation() { - return annotation; - } - - public static BeanScope fromAnnotation(Class annotation) { - if (annotation.equals(Singleton.class)) { - return SINGLETON; - } - else if (annotation.equals(ApplicationScoped.class)) { - return APPLICATION_SCOPED; - } - else if (annotation.equals(SessionScoped.class)) { - return SESSION_SCOPED; - } - else if (annotation.equals(ConversationScoped.class)) { - return CONVERSATION_SCOPED; - } - else if (annotation.equals(RequestScoped.class)) { - return REQUEST_SCOPED; - } - else if (annotation.equals(Dependent.class)) { - return DEPENDENT; - } - else { - logger.warn("fromAnnotation(): Unknown Bean scope annotation \"{}\".", annotation.getName()); - } - return UNKNOWN; - } -} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/BeanWrapper.java b/cdi/src/main/java/org/axonframework/extensions/cdi/BeanWrapper.java new file mode 100644 index 0000000..b713fa5 --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/BeanWrapper.java @@ -0,0 +1,123 @@ +package org.axonframework.extensions.cdi; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.PassivationCapable; +import javax.enterprise.util.AnnotationLiteral; +import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; +import org.axonframework.config.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BeanWrapper implements Bean, PassivationCapable { + + private static final Logger logger = LoggerFactory.getLogger( + MethodHandles.lookup().lookupClass()); + + private final String name; + private final Class clazz; + private final Supplier supplier; + + public BeanWrapper(final Class clazz, final Supplier supplier) { + this(clazz.getSimpleName(), clazz, supplier); + } + + public BeanWrapper(final String name, final Class clazz, final Supplier supplier) { + this.name = name; + this.clazz = clazz; + this.supplier = supplier; + } + + @Override + public T create(final CreationalContext context) { + return this.supplier.get(); + } + + @Override + // Mark: Is this correct? Should I be doing something additional? + public void destroy(T instance, final CreationalContext context) { + if (clazz.equals(Configuration.class)) { + logger.info("Shutting down Axon configuration."); + ((Configuration) instance).shutdown(); + } + + instance = null; + context.release(); + } + + @Override + public String getName() { + return name; + } + + @SuppressWarnings("serial") + @Override + public Set getQualifiers() { + final Set qualifiers = new HashSet<>(); + + qualifiers.add(new AnnotationLiteral() { + }); + qualifiers.add(new AnnotationLiteral() { + }); + + return qualifiers; + } + + @Override + public Class getScope() { + return ApplicationScoped.class; + } + + @Override + public Set> getStereotypes() { + return Collections.emptySet(); + } + + @Override + public Set getTypes() { + final Set types = new HashSet<>(); + + types.add(clazz); + types.add(Object.class); + + return types; + } + + @Override + public Class getBeanClass() { + return clazz; + } + + @Override + public Set getInjectionPoints() { + return Collections.emptySet(); + } + + @Override + public boolean isAlternative() { + return false; + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + // Mark: Is this sufficiently unique? Will this wrapper actually ever + // get serialized? Is this supposed to be the ID of the underlying + // bean instance or just this bean definition? + public String getId() { + return clazz.toString() + "#" + supplier.toString(); + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/CdiUtilities.java b/cdi/src/main/java/org/axonframework/extensions/cdi/CdiUtilities.java new file mode 100644 index 0000000..510f1d4 --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/CdiUtilities.java @@ -0,0 +1,118 @@ +package org.axonframework.extensions.cdi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import static java.util.Arrays.stream; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedMember; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.CDI; +import javax.inject.Named; + +public class CdiUtilities { + + /** + * Returns an object reference of a given bean. + * + * @param beanManager bean manager. + * @param bean bean. + * @param beanType bean type. + * @return Object reference. + */ + @SuppressWarnings("unchecked") + public static T getReference(final BeanManager beanManager, + final Bean bean, + final Type beanType) { + return (T) beanManager.getReference(bean, beanType, + beanManager.createCreationalContext(bean)); + } + + public static T getReference(final BeanManager beanManager, final Class clazz) { + final Set> beans = beanManager.getBeans(clazz); + final Bean bean = beanManager.resolve(beans); + + final CreationalContext creationalContext = beanManager.createCreationalContext(bean); + return (T) beanManager.getReference(bean, clazz, creationalContext); + } + + /** + * Checks whether a given bean has methods annotated with given annotation. + * + * @param at AnnotatedType to check. + * @param annotationClazz annotation class. + * @return true if at least one annotated method is present. + */ + public static final boolean hasAnnotatedMethod(final AnnotatedType at, + final Class annotationClazz) { + return at.getMethods().stream().anyMatch(m -> m.isAnnotationPresent(annotationClazz)); + } + + /** + * Checks whether a bean has a member annotated with all provided + * annotations. + * + * @param bean bean to check. + * @param classes annotation classes to check for. + * @return true if a member of a bean is annotated with all specified + * annotations. + */ + @SafeVarargs + public static boolean hasAnnotatedMember(final Bean bean, + final Class... classes) { + final Predicate hasAllAnnotations + = field -> stream(classes).allMatch(field::isAnnotationPresent); + return stream(bean.getBeanClass().getDeclaredFields()).anyMatch( + hasAllAnnotations); + } + + /** + * Retrieve the bean manager. + * + * @return bean manager, if any, or null. + */ + public static BeanManager getBeanManager() { + return CDI.current().getBeanManager(); + } + + static String extractBeanName(AnnotatedMember annotatedMember) { + Named named = annotatedMember.getAnnotation(Named.class); + + if (named != null && !"".equals(named.value())) { + return named.value(); + } + + // TODO: Should not try to derive the name of a member that does not + // have the @Named annotation on it. + return annotatedMember.getJavaMember().getName(); + } + + /** + * Returns a transitive stream of all methods of a class, for the purpose of + * scanning for methods with a given annotation. As we know Object will not + * have Axon annotations, either that or null is a reason to stop traveling + * upwards in the hierarchy. + * + * Added this because Class<>.getMethods() only returns a transitive list of + * public methods. + * + * @param clazz The starting point in the hierarchy. + * @return An empty stream for null or java.lang.Object, otherwise a stream + * of all methods (public/protected/package private/private) followed by + * those of its super, etc. + */ + // TODO: See if this is really necessary, manifestation of a bug elsewhere + // or is a quirk of CDI. Does have a performance cost. + private static Stream getDeclaredMethodsTransitive(Class clazz) { + return ((clazz == null) || clazz.equals(java.lang.Object.class)) + ? Stream.empty() + : Stream.concat(stream(clazz.getDeclaredMethods()), + getDeclaredMethodsTransitive(clazz.getSuperclass())); + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/LazyRetrievedModuleConfiguration.java b/cdi/src/main/java/org/axonframework/extensions/cdi/LazyRetrievedModuleConfiguration.java new file mode 100644 index 0000000..435b59d --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/LazyRetrievedModuleConfiguration.java @@ -0,0 +1,51 @@ +package org.axonframework.extensions.cdi; + +import org.axonframework.config.Configuration; +import org.axonframework.config.ModuleConfiguration; + +import java.util.function.Supplier; + +/** + * @author Milan Savic + */ +class LazyRetrievedModuleConfiguration implements ModuleConfiguration { + + private final Supplier delegateSupplier; + private ModuleConfiguration delegate; + + LazyRetrievedModuleConfiguration(Supplier delegateSupplier) { + this.delegateSupplier = delegateSupplier; + } + + @Override + public void initialize(Configuration config) { + getDelegate().initialize(config); + } + + @Override + public void start() { + getDelegate().start(); + } + + @Override + public void shutdown() { + getDelegate().shutdown(); + } + + @Override + public int phase() { + return getDelegate().phase(); + } + + @Override + public ModuleConfiguration unwrap() { + return getDelegate(); + } + + private ModuleConfiguration getDelegate() { + if (delegate == null) { + delegate = delegateSupplier.get(); + } + return delegate; + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/MessageHandlingBeanDefinition.java b/cdi/src/main/java/org/axonframework/extensions/cdi/MessageHandlingBeanDefinition.java new file mode 100644 index 0000000..3134634 --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/MessageHandlingBeanDefinition.java @@ -0,0 +1,70 @@ +package org.axonframework.extensions.cdi; + +import org.axonframework.commandhandling.CommandHandler; +import org.axonframework.eventhandling.EventHandler; +import org.axonframework.queryhandling.QueryHandler; + +import java.util.Optional; + +import javax.enterprise.inject.spi.Annotated; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; + +/** + * @author Milan Savic + */ +class MessageHandlingBeanDefinition { + + private final Bean bean; + private final boolean eventHandler; + private final boolean queryHandler; + private final boolean commandHandler; + + MessageHandlingBeanDefinition(Bean bean, boolean eventHandler, + boolean queryHandler, boolean commandHandler) { + this.bean = bean; + this.eventHandler = eventHandler; + this.queryHandler = queryHandler; + this.commandHandler = commandHandler; + } + + static Optional inspect(Bean bean, Annotated annotated) { + if (!(annotated instanceof AnnotatedType)) { + return Optional.empty(); + } + AnnotatedType at = (AnnotatedType) annotated; + boolean isEventHandler = CdiUtilities.hasAnnotatedMethod(at, EventHandler.class); + boolean isQueryHandler = CdiUtilities.hasAnnotatedMethod(at, QueryHandler.class); + boolean isCommandHandler = CdiUtilities.hasAnnotatedMethod(at, CommandHandler.class); + + if (isEventHandler || isQueryHandler || isCommandHandler) { + return Optional.of(new MessageHandlingBeanDefinition(bean, + isEventHandler, isQueryHandler, isCommandHandler)); + } + + return Optional.empty(); + } + + public Bean getBean() { + return bean; + } + + public boolean isEventHandler() { + return eventHandler; + } + + public boolean isQueryHandler() { + return queryHandler; + } + + public boolean isCommandHandler() { + return commandHandler; + } + + @Override + public String toString() { + return "MessageHandlingBeanDefinition with bean=" + bean + + ", eventHandler=" + eventHandler + ", queryHandler=" + + queryHandler + ", commandHandler=" + commandHandler; + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/SagaDefinition.java b/cdi/src/main/java/org/axonframework/extensions/cdi/SagaDefinition.java new file mode 100644 index 0000000..1e4be6d --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/SagaDefinition.java @@ -0,0 +1,42 @@ +package org.axonframework.extensions.cdi; + +import org.axonframework.extensions.cdi.stereotype.Saga; + +import java.util.Optional; + +/** + * @author Milan Savic + */ +class SagaDefinition { + + private final Class sagaType; + + SagaDefinition(Class sagaType) { + this.sagaType = sagaType; + } + + Class sagaType() { + return sagaType; + } + + Optional sagaStore() { + return StringUtilities.createOptional(getSagaAnnotation().sagaStore()); + } + + Optional configurationBean() { + return StringUtilities.createOptional(getSagaAnnotation().configurationBean()); + } + + boolean explicitConfiguration() { + return configurationBean().isPresent(); + } + + String configurationName() { + return configurationBean().orElse(StringUtilities.lowerCaseFirstLetter( + sagaType().getSimpleName()) + "Configuration"); + } + + private Saga getSagaAnnotation() { + return sagaType.getAnnotation(Saga.class); + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/util/StringUtilities.java b/cdi/src/main/java/org/axonframework/extensions/cdi/StringUtilities.java similarity index 54% rename from cdi/src/main/java/org/axonframework/extensions/cdi/util/StringUtilities.java rename to cdi/src/main/java/org/axonframework/extensions/cdi/StringUtilities.java index a22fa93..ff29ddf 100644 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/util/StringUtilities.java +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/StringUtilities.java @@ -1,14 +1,14 @@ -package org.axonframework.extensions.cdi.util; +package org.axonframework.extensions.cdi; import java.util.Optional; -public class StringUtilities { +class StringUtilities { - public static String lowerCaseFirstLetter(String string) { + static String lowerCaseFirstLetter(String string) { return string.substring(0, 1).toLowerCase() + string.substring(1); } - public static Optional createOptional(String value) { + static Optional createOptional(String value) { if ("".equals(value)) { return Optional.empty(); } diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/config/CdiProducer.java b/cdi/src/main/java/org/axonframework/extensions/cdi/config/CdiProducer.java deleted file mode 100644 index 23d1467..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/config/CdiProducer.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.axonframework.extensions.cdi.config; - -import org.axonframework.common.transaction.TransactionManager; -import org.axonframework.extensions.cdi.transaction.JtaTransactionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Produces; -import javax.inject.Singleton; -import java.lang.invoke.MethodHandles; - -@ApplicationScoped -public class CdiProducer { - - private static final Logger logger = LoggerFactory.getLogger( - MethodHandles.lookup().lookupClass()); - - @Produces @IfNoneDefined - @Singleton - public TransactionManager transactionManager() { - logger.info("transactionManager()"); - - return new JtaTransactionManager(); - } - -} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/config/ConfigurerProducer.java b/cdi/src/main/java/org/axonframework/extensions/cdi/config/ConfigurerProducer.java deleted file mode 100644 index 82be62a..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/config/ConfigurerProducer.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.axonframework.extensions.cdi.config; - -import org.axonframework.config.Configurer; -import org.axonframework.config.DefaultConfigurer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.invoke.MethodHandles; - -public class ConfigurerProducer { - - private static final Logger logger = LoggerFactory.getLogger( - MethodHandles.lookup().lookupClass()); - - private enum ConfigurerEnum { - INSTANCE; - - private final Configurer value; - - ConfigurerEnum() { - logger.info("instance(): Creating a new DefaultConfigurer..."); - this.value = DefaultConfigurer.defaultConfiguration(); - } - - } - - public static Configurer instance() { - logger.info("instance(): Here's your Configurer..."); - - return ConfigurerEnum.INSTANCE.value; - } -} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/config/IfNoneDefined.java b/cdi/src/main/java/org/axonframework/extensions/cdi/config/IfNoneDefined.java deleted file mode 100644 index 1c6b6d4..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/config/IfNoneDefined.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.axonframework.extensions.cdi.config; - -import javax.inject.Qualifier; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -@Target(value = {TYPE, METHOD, PARAMETER, FIELD}) - @Retention(value = RUNTIME) - @Documented - @Qualifier -public @interface IfNoneDefined { - Class value() default Class.class; -} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/eventhandling/saga/CdiResourceInjector.java b/cdi/src/main/java/org/axonframework/extensions/cdi/eventhandling/saga/CdiResourceInjector.java new file mode 100644 index 0000000..bf043c1 --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/eventhandling/saga/CdiResourceInjector.java @@ -0,0 +1,30 @@ +package org.axonframework.extensions.cdi.eventhandling.saga; + +import org.axonframework.modelling.saga.ResourceInjector; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; + +/** + * @author Milan Savic + */ +public class CdiResourceInjector implements ResourceInjector { + + private final BeanManager beanManager; + + public CdiResourceInjector(BeanManager beanManager) { + this.beanManager = beanManager; + } + + @Override + @SuppressWarnings("unchecked") + public void injectResources(Object saga) { + CreationalContext creationalContext = beanManager.createCreationalContext(null); + + AnnotatedType annotatedType = beanManager.createAnnotatedType(saga.getClass()); + InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType); + injectionTarget.inject(saga, creationalContext); + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/messagehandling/MessageHandlingBeanDefinition.java b/cdi/src/main/java/org/axonframework/extensions/cdi/messagehandling/MessageHandlingBeanDefinition.java deleted file mode 100644 index 9bb556e..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/messagehandling/MessageHandlingBeanDefinition.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.axonframework.extensions.cdi.messagehandling; - -import org.axonframework.commandhandling.CommandHandler; -import org.axonframework.eventhandling.EventHandler; -import org.axonframework.eventsourcing.EventSourcingHandler; -import org.axonframework.extensions.cdi.BeanScope; -import org.axonframework.extensions.cdi.stereotype.Aggregate; -import org.axonframework.queryhandling.QueryHandler; -import org.slf4j.Logger; - -import javax.enterprise.inject.spi.Annotated; -import javax.enterprise.inject.spi.AnnotatedType; -import javax.enterprise.inject.spi.Bean; -import java.util.Optional; - -import static java.lang.invoke.MethodHandles.lookup; -import static org.slf4j.LoggerFactory.getLogger; - -/** - * @author Milan Savic - */ -public class MessageHandlingBeanDefinition { - - private static final Logger logger = getLogger(lookup().lookupClass()); - - private final Bean bean; - private final BeanScope scope; - private boolean eventHandler = false; - private boolean eventSourcingHandler = false; - private boolean queryHandler = false; - private boolean commandHandler = false; - - private MessageHandlingBeanDefinition(Bean bean) { - this.bean = bean; - this.scope = BeanScope.fromAnnotation(bean.getScope()); - } - - public static Optional inspect(Bean bean, Annotated annotated) { - if (!(annotated instanceof AnnotatedType)) { - return Optional.empty(); - } - if (annotated.isAnnotationPresent(Aggregate.class)) { - logger.info("inspect({}, {}): Ignoring Aggregates...", bean.getBeanClass().getName(), annotated.getClass().getName()); - - return Optional.empty(); - } - - MessageHandlingBeanDefinition result = new MessageHandlingBeanDefinition(bean); - - ((AnnotatedType) annotated).getMethods().forEach(m -> { - if (m.isAnnotationPresent(QueryHandler.class)) { - logger.info("inspect({}, ...): Found an @QueryHandler annotation.", bean.getBeanClass().getName()); - - result.queryHandler = true; - } else if (m.isAnnotationPresent(EventSourcingHandler.class)) { - logger.info("inspect({}, ...): Found an @EventSourcingHandler annotation.", bean.getBeanClass().getName()); - - result.eventSourcingHandler = true; - } else if (m.isAnnotationPresent(EventHandler.class)) { - logger.info("inspect({}, ...): Found an @EventHandler annotation.", bean.getBeanClass().getName()); - - result.eventHandler = true; - } else if (m.isAnnotationPresent(CommandHandler.class)) { - logger.info("inspect({}, ...): Found an @CommandHandler annotation.", bean.getBeanClass().getName()); - - result.commandHandler = true; - } - }); - if (result.isMessageHandler()) { - logger.info("inspect(): Class {} has Scope {}.", bean.getBeanClass().getName(), result.getScope()); - } - return result.isMessageHandler() ? Optional.of(result) : Optional.empty(); - } - - public Bean getBean() { - return bean; - } - - public BeanScope getScope() { - return scope; - } - - public boolean isEventHandler() { - return eventHandler; - } - - public boolean isEventSourcingHandler() { - return eventSourcingHandler; - } - - public boolean isQueryHandler() { - return queryHandler; - } - - public boolean isCommandHandler() { - return commandHandler; - } - - public boolean isMessageHandler() { - return eventHandler || eventSourcingHandler || queryHandler || commandHandler; - } - - @Override - public String toString() { - return "MessageHandlingBeanDefinition with bean=" + bean - + ", eventHandler=" + eventHandler + ", queryHandler=" - + queryHandler + ", commandHandler=" + commandHandler; - } -} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/messaging/annotation/CdiParameterResolver.java b/cdi/src/main/java/org/axonframework/extensions/cdi/messaging/annotation/CdiParameterResolver.java new file mode 100644 index 0000000..8c02cc9 --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/messaging/annotation/CdiParameterResolver.java @@ -0,0 +1,35 @@ +package org.axonframework.extensions.cdi.messaging.annotation; + +import org.axonframework.extensions.cdi.CdiUtilities; +import org.axonframework.messaging.annotation.ParameterResolver; + +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import java.lang.reflect.Type; +import org.axonframework.messaging.Message; + +public class CdiParameterResolver implements ParameterResolver { + + private final BeanManager beanManager; + private final Bean bean; + private final Type type; + + public CdiParameterResolver(final BeanManager beanManager, + final Bean bean, final Type type) { + this.beanManager = beanManager; + this.bean = bean; + this.type = type; + } + + @Override + @SuppressWarnings("rawtypes") + public Object resolveParameterValue(final Message message) { + return CdiUtilities.getReference(beanManager, bean, type); + } + + @Override + @SuppressWarnings("rawtypes") + public boolean matches(final Message message) { + return true; + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/messaging/annotation/CdiParameterResolverFactory.java b/cdi/src/main/java/org/axonframework/extensions/cdi/messaging/annotation/CdiParameterResolverFactory.java new file mode 100644 index 0000000..58a863d --- /dev/null +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/messaging/annotation/CdiParameterResolverFactory.java @@ -0,0 +1,68 @@ +package org.axonframework.extensions.cdi.messaging.annotation; + +import java.lang.invoke.MethodHandles; +import org.axonframework.extensions.cdi.CdiUtilities; +import org.axonframework.common.Priority; +import org.axonframework.messaging.annotation.ParameterResolver; +import org.axonframework.messaging.annotation.ParameterResolverFactory; + +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import java.lang.reflect.Executable; +import java.lang.reflect.Parameter; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Parameter resolver factory for instantiating Axon artifacts inside of a CDI + * context. + * + * @author Simon Zambrovski + */ +@Priority(Priority.LOW) +public class CdiParameterResolverFactory implements ParameterResolverFactory { + + private final static Logger logger = LoggerFactory.getLogger( + MethodHandles.lookup().lookupClass()); + + private final BeanManager beanManager; + + public CdiParameterResolverFactory() { + this.beanManager = CdiUtilities.getBeanManager(); + } + + @Override + public ParameterResolver createInstance(final Executable executable, + final Parameter[] parameters, final int parameterIndex) { + final Parameter parameter = parameters[parameterIndex]; + + if (this.beanManager == null) { + logger.error( + "BeanManager was null. This is a fatal error, an instance of {} {} is not created.", + parameter.getType(), + parameter.getAnnotations()); + return null; + } + + logger.trace("Create instance for {} {}.", parameter.getType(), + parameter.getAnnotations()); + + final Set> beansFound = beanManager.getBeans(parameter.getType(), + parameter.getAnnotations()); + + if (beansFound.isEmpty()) { + return null; + } else if (beansFound.size() > 1) { + if (logger.isWarnEnabled()) { + logger.warn("Ambiguous reference for parameter type {} with qualifiers {}.", + parameter.getType().getName(), parameter.getAnnotations()); + } + + return null; + } else { + return new CdiParameterResolver(beanManager, + beansFound.iterator().next(), parameter.getType()); + } + } +} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/stereotype/Saga.java b/cdi/src/main/java/org/axonframework/extensions/cdi/stereotype/Saga.java index 4eaa630..b3fa273 100644 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/stereotype/Saga.java +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/stereotype/Saga.java @@ -1,10 +1,10 @@ package org.axonframework.extensions.cdi.stereotype; -import javax.enterprise.inject.Stereotype; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.enterprise.inject.Stereotype; /** * @author Milan Savic diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/transaction/JtaTransaction.java b/cdi/src/main/java/org/axonframework/extensions/cdi/transaction/JtaTransaction.java index 0b39a30..0db08cd 100644 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/transaction/JtaTransaction.java +++ b/cdi/src/main/java/org/axonframework/extensions/cdi/transaction/JtaTransaction.java @@ -1,14 +1,20 @@ package org.axonframework.extensions.cdi.transaction; +import java.lang.invoke.MethodHandles; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.SystemException; +import javax.transaction.TransactionSynchronizationRegistry; +import javax.transaction.UserTransaction; import org.axonframework.common.transaction.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.transaction.*; -import java.lang.invoke.MethodHandles; - public class JtaTransaction implements Transaction { private static final String USER_TRANSACTION_LOCATION = "java:comp/UserTransaction"; @@ -50,7 +56,7 @@ private void detectContext() { // and still be associated with current thread. // See WFLY-4327 int status = userTransaction.getStatus(); - if(status== Status.STATUS_ROLLEDBACK || status== Status.STATUS_MARKED_ROLLBACK) + if(status==Status.STATUS_ROLLEDBACK || status==Status.STATUS_MARKED_ROLLBACK) { logger.error("Cleanup of transaction that has been rolled back previously."); userTransaction.rollback(); diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/util/CdiUtilities.java b/cdi/src/main/java/org/axonframework/extensions/cdi/util/CdiUtilities.java deleted file mode 100644 index 2d59dde..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/util/CdiUtilities.java +++ /dev/null @@ -1,201 +0,0 @@ -package org.axonframework.extensions.cdi.util; - -import javax.enterprise.inject.spi.*; -import javax.inject.Named; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Optional; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import static java.util.Arrays.stream; - -public class CdiUtilities { - - /** - * Returns an object reference of a given bean. - * - * @param beanManager bean manager. - * @param bean bean. - * @param beanType bean type. - * @return Object reference. - */ - @SuppressWarnings("unchecked") - public static T getReference(final BeanManager beanManager, - final Bean bean, - final Type beanType) { - return (T) beanManager.getReference(bean, beanType, - beanManager.createCreationalContext(bean)); - } - -// public static T getReference(final BeanManager beanManager, final Class clazz) { -// final Set> beans = beanManager.getBeans(clazz); -// final Bean bean = beanManager.resolve(beans); -// -// final CreationalContext creationalContext = beanManager.createCreationalContext(bean); -// return (T) beanManager.getReference(bean, clazz, creationalContext); -// } - - /** - * Checks whether a given bean has methods annotated with given annotation. - * - * @param at AnnotatedType to check. - * @param annotationClazz annotation class. - * @return true if at least one annotated method is present. - */ - public static final boolean hasAnnotatedMethod(final AnnotatedType at, - final Class annotationClazz) { - return at.getMethods().stream().anyMatch(m -> m.isAnnotationPresent(annotationClazz)); - } - - /** - * Checks whether a bean has a member annotated with all provided - * annotations. - * - * @param bean bean to check. - * @param classes annotation classes to check for. - * @return true if a member of a bean is annotated with all specified - * annotations. - */ - @SafeVarargs - public static boolean hasAnnotatedMember(final Bean bean, - final Class... classes) { - final Predicate hasAllAnnotations - = field -> stream(classes).allMatch(field::isAnnotationPresent); - return stream(bean.getBeanClass().getDeclaredFields()).anyMatch( - hasAllAnnotations); - } - - /** - * Retrieve the bean manager. - * - * @return bean manager, if any, or null. - */ - public static BeanManager getBeanManager() { - return CDI.current().getBeanManager(); - } - - /* - * Resolve the beans and return the (typecasted) instance as an {@link Optional}. - */ - @SuppressWarnings("unchecked") - private static Optional resolve(final BeanManager mgr, final Set> beans, final Class clazz) { - if ((beans == null) || beans.isEmpty()) { - return Optional.empty(); - } - Bean bean = mgr.resolve(beans); - return Optional.ofNullable((T)mgr.getReference(bean, clazz, mgr.createCreationalContext(bean))); - } - - /** - * - * @param mgr - * @param bean - * @param clazz - * @param - * @return - */ - @SuppressWarnings("unchecked") - public static Optional getBean(BeanManager mgr, Bean bean, Class clazz) { - return Optional.ofNullable((T)mgr.getReference(bean, clazz, mgr.createCreationalContext(bean))); - } - - /** - * - * @param mgr - * @param clazz - * @return - */ - public static Optional> resolveBean(final BeanManager mgr, final Class clazz) { - Set> beans = mgr.getBeans(clazz); - if ((beans == null) || beans.isEmpty()) { - return Optional.empty(); - } - return Optional.ofNullable(mgr.resolve(beans)); - } - - /** - * Return a bean, given its class, as an {@link Optional}, empty if no bean instance found. - * - * @param clazz the required class of the bean. - * @param the class of the bean, to fix the template parameter. - * @return an {@link Optional} of the bean. - */ - public static Optional getInstance(final BeanManager mgr, final Class clazz) { - return resolve(mgr, mgr.getBeans(clazz), clazz); - } - - /** - * Return a bean, given its class, as an {@link Optional}, empty if no bean instance found. - * - * @param clazz the required class of the bean. - * @param the class of the bean, to fix the template parameter. - * @return an {@link Optional} of the bean. - */ - public static Optional getInstance(final Class clazz) { - return getInstance(getBeanManager(), clazz); - } - - /** - * Return a named bean as an {@link Optional}, empty if no bean instance found. - * - * @param mgr the {@link} BeanManager to query. - * @param name the name of the bean. - * @param clazz the required class of the bean. - * @param the class of the bean, to fix the template parameter. - * @return an {@link Optional} of the bean. - */ - public static Optional getInstance(final BeanManager mgr, final String name, final Class clazz) { - return resolve(mgr, mgr.getBeans(name), clazz); - } - - /** - * Return a named bean as an {@link Optional}, empty if no bean instance found. - * - * @param name the name of the bean. - * @param clazz the required class of the bean. - * @param the class of the bean, to fix the template parameter. - * @return an {@link Optional} of the bean. - */ - public static Optional getInstance(final String name, final Class clazz) { - return getInstance(getBeanManager(), name, clazz); - } - - public static String extractBeanName(AnnotatedMember annotatedMember) { - Named named = annotatedMember.getAnnotation(Named.class); - - if (named != null && !"".equals(named.value())) { - return named.value(); - } - - // TODO: Should not try to derive the name of a member that does not - // have the @Named annotation on it. - return annotatedMember.getJavaMember().getName(); - } - - /** - * Returns a transitive stream of all methods of a class, for the purpose of - * scanning for methods with a given annotation. As we know Object will not - * have Axon annotations, either that or null is a reason to stop traveling - * upwards in the hierarchy. - * - * Added this because Class<>.getMethods() only returns a transitive list of - * public methods. - * - * @param clazz The starting point in the hierarchy. - * @return An empty stream for null or java.lang.Object, otherwise a stream - * of all methods (public/protected/package private/private) followed by - * those of its super, etc. - */ - // TODO: See if this is really necessary, manifestation of a bug elsewhere - // or is a quirk of CDI. Does have a performance cost. - private static Stream getDeclaredMethodsTransitive(Class clazz) { - return ((clazz == null) || clazz.equals(Object.class)) - ? Stream.empty() - : Stream.concat(stream(clazz.getDeclaredMethods()), - getDeclaredMethodsTransitive(clazz.getSuperclass())); - } -} diff --git a/cdi/src/main/java/org/axonframework/extensions/cdi/util/SupplierBasedLifeCycle.java b/cdi/src/main/java/org/axonframework/extensions/cdi/util/SupplierBasedLifeCycle.java deleted file mode 100644 index f4e2aa6..0000000 --- a/cdi/src/main/java/org/axonframework/extensions/cdi/util/SupplierBasedLifeCycle.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.axonframework.extensions.cdi.util; - -import org.apache.deltaspike.core.util.metadata.builder.ContextualLifecycle; - -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.inject.spi.Bean; -import java.util.function.Supplier; - -public class SupplierBasedLifeCycle implements ContextualLifecycle { - - private final Supplier supplier; - - public SupplierBasedLifeCycle(Supplier supplier) { - this.supplier = supplier; - } - - @Override - public T create(Bean bean, CreationalContext creationalContext) { - return supplier.get(); - } - - @Override - public void destroy(Bean bean, T t, CreationalContext creationalContext) { - // Donothing - } -} diff --git a/cdi/src/main/resources/META-INF/services/org.axonframework.messaging.annotation.ParameterResolverFactory b/cdi/src/main/resources/META-INF/services/org.axonframework.messaging.annotation.ParameterResolverFactory new file mode 100644 index 0000000..8da2d89 --- /dev/null +++ b/cdi/src/main/resources/META-INF/services/org.axonframework.messaging.annotation.ParameterResolverFactory @@ -0,0 +1 @@ +org.axonframework.extensions.cdi.messaging.annotation.CdiParameterResolverFactory \ No newline at end of file diff --git a/cdi/src/test/java/org/axonframework/extensions/cdi/AutoWiredEventSourcedAggregateTest.java b/cdi/src/test/java/org/axonframework/extensions/cdi/AutoWiredEventSourcedAggregateTest.java deleted file mode 100644 index ec22dc5..0000000 --- a/cdi/src/test/java/org/axonframework/extensions/cdi/AutoWiredEventSourcedAggregateTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.axonframework.extensions.cdi; - -import org.axonframework.commandhandling.CommandHandler; -import org.axonframework.common.jpa.EntityManagerProvider; -import org.axonframework.config.Configuration; -import org.axonframework.config.EventProcessingModule; -import org.axonframework.eventsourcing.EventSourcingHandler; -import org.axonframework.eventsourcing.EventSourcingRepository; -import org.axonframework.eventsourcing.eventstore.EventStorageEngine; -import org.axonframework.eventsourcing.eventstore.inmemory.InMemoryEventStorageEngine; -import org.axonframework.extensions.cdi.stereotype.Aggregate; -import org.axonframework.modelling.command.Repository; -import org.jboss.weld.junit4.WeldInitiator; -import org.junit.Rule; -import org.junit.Test; - -import javax.enterprise.inject.Produces; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.persistence.Id; - -import static org.axonframework.modelling.command.AggregateLifecycle.apply; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; - - -public class AutoWiredEventSourcedAggregateTest { - - @Rule - public WeldInitiator weld = WeldInitiator - .from(WeldInitiator.createWeld().enableDiscovery()) - .inject(this) - .build(); - - @Inject - private Repository myAggregateRepository; - - @Inject - private Configuration configuration; - - @Test - public void testAggregateIsWiredUsingStateStorage() { - assertTrue(/*EventSourcing*/Repository.class.isAssignableFrom(myAggregateRepository.getClass())); - assertEquals(EventSourcingRepository.class, configuration.repository(Context.MyAggregate.class).getClass()); - } - - @Singleton - public static class Context { - - @Produces - public EventStorageEngine eventStorageEngine() { - return new InMemoryEventStorageEngine(); - } - - @Produces - public EntityManagerProvider entityManagerProvider() { - return mock(EntityManagerProvider.class); - } - - @Produces - public EventProcessingModule eventProcessingConfiguration() { - return new EventProcessingModule(); - } - - @Aggregate - public static class MyAggregate { - - @Id - private String id; - - @CommandHandler - public void handle(Long command) { - apply(command); - } - - @CommandHandler - public void handle(String command) { - } - - @EventSourcingHandler - public void on(Long event) { - this.id = Long.toString(event); - } - - @EventSourcingHandler - public void on(String event) { - fail("Event Handler on aggregate shouldn't be invoked"); - } - } - } - - public static class SomeEvent { - - private final String id; - - public SomeEvent(String id) { - this.id = id; - } - - public String getId() { - return id; - } - } - - -} diff --git a/cdi/src/test/java/org/axonframework/extensions/cdi/BeanWrapperTest.java b/cdi/src/test/java/org/axonframework/extensions/cdi/BeanWrapperTest.java new file mode 100644 index 0000000..ef0aca5 --- /dev/null +++ b/cdi/src/test/java/org/axonframework/extensions/cdi/BeanWrapperTest.java @@ -0,0 +1,101 @@ +package org.axonframework.extensions.cdi; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.function.Supplier; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Default; +import javax.enterprise.util.AnnotationLiteral; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class BeanWrapperTest { + + private final Supplier supplier = () -> new Foo(); + private final BeanWrapper testee = new BeanWrapper<>(Foo.class, supplier); + + @Test + public void testId() { + assertEquals(Foo.class.toString() + "#" + supplier.toString(), testee.getId()); + } + + @Test + public void testCreate() { + assertEquals(new Foo(), testee.create(null)); + } + + // Mark: How do you think I can test this? + @Test + public void testDestroy() { + // TODO. + } + + @Test + public void testName() { + assertEquals(Foo.class.getSimpleName(), testee.getName()); + } + + @Test + public void testGetStereotypes() { + assertTrue(testee.getStereotypes().isEmpty()); + } + + @Test + public void testGetTypes() { + assertEquals(new HashSet<>(Arrays.asList(Foo.class, Object.class)), + testee.getTypes()); + } + + @Test + public void testBeanClass() { + assertEquals(Foo.class, testee.getBeanClass()); + } + + @Test + public void testScope() { + assertEquals(ApplicationScoped.class, testee.getScope()); + } + + @Test + public void testQualifiers() { + assertEquals(new HashSet<>(Arrays.asList(new AnnotationLiteral() { + }, new AnnotationLiteral() { + })), testee.getQualifiers()); + } + + @Test + public void testInjectionPoints() { + assertTrue(testee.getInjectionPoints().isEmpty()); + } + + @Test + public void testIsAlternative() { + assertFalse(testee.isAlternative()); + } + + @Test + public void testIsNullable() { + assertFalse(testee.isNullable()); + } + + private static class Foo { + + @Override + public boolean equals(Object obj) { + return (obj instanceof Foo); + } + + @Override + public int hashCode() { + return 7; + } + + @Override + public String toString() { + return "Foo"; + } + } +} diff --git a/cdi/src/test/java/org/axonframework/extensions/cdi/CdiUtilitiesTest.java b/cdi/src/test/java/org/axonframework/extensions/cdi/CdiUtilitiesTest.java new file mode 100644 index 0000000..b2c4959 --- /dev/null +++ b/cdi/src/test/java/org/axonframework/extensions/cdi/CdiUtilitiesTest.java @@ -0,0 +1,198 @@ +package org.axonframework.extensions.cdi; + +import java.io.Serializable; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Set; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Inject; +import org.jboss.weld.junit4.WeldInitiator; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import org.junit.Rule; +import org.junit.Test; + +public class CdiUtilitiesTest { + + @Rule + public WeldInitiator weld = WeldInitiator.from(Foo.class).inject(this).build(); + + @Inject + private BeanManager beanManager; + + @Inject + Foo foo; + + @Test + public void testGetBeanManager() { + assertNotNull(CdiUtilities.getBeanManager()); + } + + @Test + public void testGetReference() { + Set> beansFound = beanManager.getBeans(Foo.class, + new AnnotationLiteral() { + }); + assertSame(foo, CdiUtilities.getReference(beanManager, + beansFound.iterator().next(), Foo.class)); + } + + @Test + public void hasAnnotatedMember_true() { + assertTrue(CdiUtilities.hasAnnotatedMember(new HasAnnotatedField(), + FirstFieldAnnotation.class)); + } + + @Test + public void hasAnnotatedMember_false() { + assertFalse(CdiUtilities.hasAnnotatedMember(new HasNoAnnotatedField(), + FirstFieldAnnotation.class)); + } + + @Test + public void hasAnnotatedMember_both_false() { + assertFalse(CdiUtilities.hasAnnotatedMember(new HasAnnotatedField(), + FirstFieldAnnotation.class, SecondFieldAnnotation.class)); + } + + @Test + public void hasAnnotatedMember_both_true() { + assertTrue(CdiUtilities.hasAnnotatedMember(new HasBothFieldAnnotations(), + FirstFieldAnnotation.class, SecondFieldAnnotation.class)); + } + +/*X TODO Reza: hasAnnotatedMethod now takes an AnnotatedType not a Bean. + @Test + public void hasAnnotatedMethod_true() { + assertTrue(CdiUtilities.hasAnnotatedMethod(new HasMethodAnnotation(), + FirstMethodAnnotation.class)); + } + + @Test + public void hasAnnotatedMethodInParent_true() { + assertTrue(CdiUtilities.hasAnnotatedMethod(new InheritAnnotatedMethod(), + FirstMethodAnnotation.class)); + } + + @Test + public void hasAnnotatedPrivateMethod_true() { + assertTrue(CdiUtilities.hasAnnotatedMethod(new HasPrivateMethodAnnotation(), + FirstMethodAnnotation.class)); + } + + @Test + public void hasAnnotatedPrivateMethodInParent_true() { + assertTrue(CdiUtilities.hasAnnotatedMethod(new InheritAnnotatedPrivateMethod(), + FirstMethodAnnotation.class)); + } + + @Test + public void hasAnnotatedMethod_false() { + assertFalse(CdiUtilities.hasAnnotatedMethod(new HasMethodAnnotation(), + SecondMethodAnnotation.class)); + } +*/ + + @ApplicationScoped + private static class Foo implements Serializable { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface FirstFieldAnnotation { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface SecondFieldAnnotation { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface FirstMethodAnnotation { + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface SecondMethodAnnotation { + } + + public static class HasAnnotatedField extends BeanWrapper { + + @FirstFieldAnnotation + protected String name; + + public HasAnnotatedField() { + super(HasAnnotatedField.class, HasAnnotatedField::new); + } + } + + public static class HasMethodAnnotation extends BeanWrapper { + + public HasMethodAnnotation() { + super(HasMethodAnnotation.class, HasMethodAnnotation::new); + } + + @FirstMethodAnnotation + public void foo() { + + } + } + + public static class InheritAnnotatedMethod extends HasMethodAnnotation { + public void bar() { + + } + } + + public static class HasPrivateMethodAnnotation extends BeanWrapper { + + public HasPrivateMethodAnnotation() { + super(HasMethodAnnotation.class, HasMethodAnnotation::new); + } + + @FirstMethodAnnotation + private void foo() { + + } + } + + public static class InheritAnnotatedPrivateMethod extends HasPrivateMethodAnnotation { + public void bar() { + + } + } + + public static class HasBothFieldAnnotations extends BeanWrapper { + + @FirstFieldAnnotation + @SecondFieldAnnotation + protected String name; + + public HasBothFieldAnnotations() { + super(HasBothFieldAnnotations.class, HasBothFieldAnnotations::new); + } + } + + public static class InheritAnnotatedField extends HasAnnotatedField { + + private String other; + } + + public static class HasNoAnnotatedField extends BeanWrapper { + + private String name; + + public HasNoAnnotatedField() { + super(HasNoAnnotatedField.class, HasNoAnnotatedField::new); + } + } +} diff --git a/cdi/src/test/java/org/axonframework/extensions/cdi/command/CommandTest.java b/cdi/src/test/java/org/axonframework/extensions/cdi/command/CommandTest.java new file mode 100644 index 0000000..90d4c31 --- /dev/null +++ b/cdi/src/test/java/org/axonframework/extensions/cdi/command/CommandTest.java @@ -0,0 +1,115 @@ +package org.axonframework.extensions.cdi.command; + +import org.axonframework.extensions.cdi.stereotype.Aggregate; +import org.axonframework.commandhandling.CommandHandler; +import org.axonframework.eventsourcing.EventSourcingHandler; +import org.axonframework.modelling.command.AggregateIdentifier; +import org.axonframework.modelling.command.TargetAggregateIdentifier; +import org.axonframework.test.aggregate.AggregateTestFixture; +import org.jboss.weld.junit4.WeldInitiator; +import org.junit.*; + +import javax.faces.bean.ApplicationScoped; + +import static org.axonframework.modelling.command.AggregateLifecycle.apply; + +public class CommandTest { + + @Rule + public WeldInitiator weld = WeldInitiator + .from(CommandTest.Account.class) + .inject(this) + .build(); + + private AggregateTestFixture account; + + @Before + public void setup() { + account = new AggregateTestFixture<>(Account.class); + } + + @Test + public void testAccountCreate() { + account.givenNoPriorActivity() + .when(new CreateAccountCommand("4711", 100.00)) + .expectEvents(new AccountCreatedEvent("4711", 100.00)); + } + + public static class CreateAccountCommand { + + @TargetAggregateIdentifier + private final String accountId; + private final Double overdraftLimit; + + public CreateAccountCommand(String accountId, Double overdraftLimit) { + this.accountId = accountId; + this.overdraftLimit = overdraftLimit; + } + + public String getAccountId() { + return accountId; + } + + public Double getOverdraftLimit() { + return overdraftLimit; + } + + @Override + public String toString() { + return "Create Account Command with accountId=" + accountId + + ", overdraftLimit=" + overdraftLimit; + } + } + + public static class AccountCreatedEvent { + + private final String accountId; + private final Double overdraftLimit; + + public AccountCreatedEvent(String accountId, Double overdraftLimit) { + this.accountId = accountId; + this.overdraftLimit = overdraftLimit; + } + + public String getAccountId() { + return accountId; + } + + public Double getOverdraftLimit() { + return overdraftLimit; + } + + @Override + public String toString() { + return "Account Created Event with accountId=" + accountId + + ", overdraftLimit=" + overdraftLimit; + } + } + + @ApplicationScoped + @Aggregate + public static class Account { + + @AggregateIdentifier + private String accountId; + + @SuppressWarnings("unused") + private Double overdraftLimit; + + public Account() { + // Empty constructor needed for CDI proxying. + } + + @CommandHandler + public Account(final CreateAccountCommand command) { + apply(new AccountCreatedEvent(command.getAccountId(), + command.getOverdraftLimit())); + } + + @EventSourcingHandler + public void on(AccountCreatedEvent event) { + this.accountId = event.getAccountId(); + this.overdraftLimit = event.getOverdraftLimit(); + } + } +} diff --git a/cdi/src/test/resources/META-INF/beans.xml b/cdi/src/test/resources/META-INF/beans.xml deleted file mode 100644 index 3e13df0..0000000 --- a/cdi/src/test/resources/META-INF/beans.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/AccountApplication.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/AccountApplication.java index d81c25a..335b7e0 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/AccountApplication.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/AccountApplication.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javaee; +package org.axonframework.extensions.cdi.example.javaee; import java.lang.invoke.MethodHandles; import java.util.UUID; @@ -7,7 +7,7 @@ import javax.ejb.Singleton; import javax.ejb.Startup; import javax.inject.Inject; -import org.axonframework.cdi.example.javaee.command.CreateAccountCommand; +import org.axonframework.extensions.cdi.example.javaee.command.CreateAccountCommand; import org.axonframework.commandhandling.gateway.CommandGateway; import org.axonframework.eventhandling.EventBus; import org.axonframework.eventhandling.interceptors.EventLoggingInterceptor; diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/Account.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/Account.java index 0185676..8956d02 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/Account.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/Account.java @@ -1,9 +1,9 @@ -package org.axonframework.cdi.example.javaee.command; +package org.axonframework.extensions.cdi.example.javaee.command; import java.lang.invoke.MethodHandles; import java.util.logging.Level; import java.util.logging.Logger; -import org.axonframework.cdi.stereotype.Aggregate; +import org.axonframework.extensions.cdi.stereotype.Aggregate; import org.axonframework.commandhandling.CommandHandler; import org.axonframework.commandhandling.model.AggregateIdentifier; import static org.axonframework.commandhandling.model.AggregateLifecycle.apply; diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/AccountCreatedEvent.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/AccountCreatedEvent.java index 9f4da07..b80f3c2 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/AccountCreatedEvent.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/AccountCreatedEvent.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javaee.command; +package org.axonframework.extensions.cdi.example.javaee.command; public class AccountCreatedEvent { diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/CreateAccountCommand.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/CreateAccountCommand.java index f9bd411..9aac2d6 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/CreateAccountCommand.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/command/CreateAccountCommand.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javaee.command; +package org.axonframework.extensions.cdi.example.javaee.command; import org.axonframework.commandhandling.TargetAggregateIdentifier; diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/configuration/AxonConfiguration.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/configuration/AxonConfiguration.java index 3d145b4..75c2c6e 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/configuration/AxonConfiguration.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/configuration/AxonConfiguration.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javaee.configuration; +package org.axonframework.extensions.cdi.example.javaee.configuration; import java.io.Serializable; import java.lang.invoke.MethodHandles; @@ -9,7 +9,7 @@ import javax.naming.InitialContext; import javax.naming.NamingException; import javax.persistence.EntityManager; -import org.axonframework.cdi.transaction.JtaTransactionManager; +import org.axonframework.extensions.cdi.transaction.JtaTransactionManager; import org.axonframework.common.jpa.EntityManagerProvider; import org.axonframework.common.jpa.SimpleEntityManagerProvider; import org.axonframework.common.transaction.TransactionManager; diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/AccountProjection.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/AccountProjection.java index dc62d7d..d6a055c 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/AccountProjection.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/AccountProjection.java @@ -1,11 +1,11 @@ -package org.axonframework.cdi.example.javaee.query; +package org.axonframework.extensions.cdi.example.javaee.query; import java.lang.invoke.MethodHandles; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import org.axonframework.cdi.example.javaee.command.AccountCreatedEvent; +import org.axonframework.extensions.cdi.example.javaee.command.AccountCreatedEvent; import org.axonframework.eventhandling.EventBus; import org.axonframework.eventhandling.EventHandler; diff --git a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/GenericEventListener.java b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/GenericEventListener.java index 85d3790..bdf7cfc 100644 --- a/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/GenericEventListener.java +++ b/example/javaee/src/main/java/org/axonframework/cdi/example/javaee/query/GenericEventListener.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javaee.query; +package org.axonframework.extensions.cdi.example.javaee.query; import java.lang.invoke.MethodHandles; import java.util.logging.Level; diff --git a/example/javaee/src/main/resources/META-INF/persistence.xml b/example/javaee/src/main/resources/META-INF/persistence.xml index fafc460..0a26fda 100644 --- a/example/javaee/src/main/resources/META-INF/persistence.xml +++ b/example/javaee/src/main/resources/META-INF/persistence.xml @@ -4,14 +4,9 @@ - org.axonframework.eventsourcing.eventstore.AbstractEventEntry - org.axonframework.eventsourcing.eventstore.AbstractDomainEventEntry - org.axonframework.eventsourcing.eventstore.AbstractSequencedDomainEventEntry org.axonframework.eventsourcing.eventstore.jpa.DomainEventEntry - org.axonframework.eventsourcing.eventstore.AbstractSnapshotEventEntry org.axonframework.eventsourcing.eventstore.jpa.SnapshotEventEntry org.axonframework.eventhandling.saga.repository.jpa.AssociationValueEntry - org.axonframework.eventhandling.saga.repository.jpa.AbstractSagaEntry org.axonframework.eventhandling.saga.repository.jpa.SagaEntry diff --git a/example/javaee/src/test/java/org/axonframework/cdi/example/javaee/command/AccountTest.java b/example/javaee/src/test/java/org/axonframework/cdi/example/javaee/command/AccountTest.java index d3b59e1..70a348c 100644 --- a/example/javaee/src/test/java/org/axonframework/cdi/example/javaee/command/AccountTest.java +++ b/example/javaee/src/test/java/org/axonframework/cdi/example/javaee/command/AccountTest.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javaee.command; +package org.axonframework.extensions.cdi.example.javaee.command; import org.axonframework.test.aggregate.AggregateTestFixture; import org.junit.Test; diff --git a/example/javase/pom.xml b/example/javase/pom.xml index 6896466..d879cc8 100644 --- a/example/javase/pom.xml +++ b/example/javase/pom.xml @@ -86,7 +86,7 @@ true libs/ - org.axonframework.cdi.example.javase.AccountApplication + org.axonframework.extensions.cdi.example.javase.AccountApplication @@ -96,7 +96,7 @@ exec-maven-plugin 1.6.0 - org.axonframework.cdi.example.javase.AccountApplication + org.axonframework.extensions.cdi.example.javase.AccountApplication diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/AccountApplication.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/AccountApplication.java index 7f2c923..a296705 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/AccountApplication.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/AccountApplication.java @@ -1,10 +1,10 @@ -package org.axonframework.cdi.example.javase; +package org.axonframework.extensions.cdi.example.javase; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.se.SeContainer; import javax.enterprise.inject.se.SeContainerInitializer; import javax.inject.Inject; -import org.axonframework.cdi.example.javase.command.CreateAccountCommand; +import org.axonframework.extensions.cdi.example.javase.command.CreateAccountCommand; import org.axonframework.commandhandling.gateway.CommandGateway; import org.axonframework.eventhandling.EventBus; import org.axonframework.eventhandling.interceptors.EventLoggingInterceptor; diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/Account.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/Account.java index aa09755..8afb0e2 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/Account.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/Account.java @@ -1,9 +1,9 @@ -package org.axonframework.cdi.example.javase.command; +package org.axonframework.extensions.cdi.example.javase.command; import java.lang.invoke.MethodHandles; import java.util.logging.Level; import java.util.logging.Logger; -import org.axonframework.cdi.stereotype.Aggregate; +import org.axonframework.extensions.cdi.stereotype.Aggregate; import org.axonframework.commandhandling.CommandHandler; import org.axonframework.commandhandling.model.AggregateIdentifier; import static org.axonframework.commandhandling.model.AggregateLifecycle.apply; diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/AccountCreatedEvent.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/AccountCreatedEvent.java index e26cd0b..1f9df6e 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/AccountCreatedEvent.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/AccountCreatedEvent.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javase.command; +package org.axonframework.extensions.cdi.example.javase.command; public class AccountCreatedEvent { diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/CreateAccountCommand.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/CreateAccountCommand.java index 45b429a..23b4c2e 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/CreateAccountCommand.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/command/CreateAccountCommand.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javase.command; +package org.axonframework.extensions.cdi.example.javase.command; import org.axonframework.commandhandling.TargetAggregateIdentifier; diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/configuration/AxonConfiguration.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/configuration/AxonConfiguration.java index 35ae0b2..8f06245 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/configuration/AxonConfiguration.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/configuration/AxonConfiguration.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javase.configuration; +package org.axonframework.extensions.cdi.example.javase.configuration; import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/AccountProjection.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/AccountProjection.java index 61bfccc..6a37243 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/AccountProjection.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/AccountProjection.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javase.query; +package org.axonframework.extensions.cdi.example.javase.query; import java.lang.invoke.MethodHandles; import java.util.HashMap; @@ -6,7 +6,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.ApplicationScoped; -import org.axonframework.cdi.example.javase.command.AccountCreatedEvent; +import org.axonframework.extensions.cdi.example.javase.command.AccountCreatedEvent; import org.axonframework.eventhandling.EventHandler; import org.axonframework.queryhandling.QueryHandler; diff --git a/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/GenericEventListener.java b/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/GenericEventListener.java index ab990b7..075f548 100644 --- a/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/GenericEventListener.java +++ b/example/javase/src/main/java/org/axonframework/cdi/example/javase/query/GenericEventListener.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javase.query; +package org.axonframework.extensions.cdi.example.javase.query; import java.lang.invoke.MethodHandles; import java.util.logging.Level; diff --git a/example/javase/src/test/java/org/axonframework/cdi/example/javase/command/AccountTest.java b/example/javase/src/test/java/org/axonframework/cdi/example/javase/command/AccountTest.java index f9e5c39..5259c50 100644 --- a/example/javase/src/test/java/org/axonframework/cdi/example/javase/command/AccountTest.java +++ b/example/javase/src/test/java/org/axonframework/cdi/example/javase/command/AccountTest.java @@ -1,4 +1,4 @@ -package org.axonframework.cdi.example.javase.command; +package org.axonframework.extensions.cdi.example.javase.command; import org.axonframework.test.aggregate.AggregateTestFixture; import org.junit.Test; diff --git a/example/thorntail/src/main/java/org/axonframework/extension/example/swarm/aggregate/Customer.java b/example/thorntail/src/main/java/org/axonframework/extension/example/swarm/aggregate/Customer.java index ef2561f..1a49da7 100644 --- a/example/thorntail/src/main/java/org/axonframework/extension/example/swarm/aggregate/Customer.java +++ b/example/thorntail/src/main/java/org/axonframework/extension/example/swarm/aggregate/Customer.java @@ -2,7 +2,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.axonframework.cdi.stereotype.Aggregate; +import org.axonframework.extensions.cdi.stereotype.Aggregate; import org.axonframework.commandhandling.CommandHandler; import org.axonframework.commandhandling.model.AggregateIdentifier; import org.axonframework.eventsourcing.EventSourcingHandler; diff --git a/pom.xml b/pom.xml index 969aae3..163793b 100644 --- a/pom.xml +++ b/pom.xml @@ -15,9 +15,7 @@ ~ limitations under the License. --> - + org.axonframework.extensions.cdi axon-cdi-parent @@ -48,70 +46,35 @@ https://github.com/AxonFramework/extension-cdi/issues - - - release-sign-artifacts - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - - sign - - - - - - - - - - coverage - - - - org.jacoco - jacoco-maven-plugin - 0.8.1 - - - - - prepare-agent - - - - report - prepare-package - - report - - - - - - - - - - UTF-8 - 4.1 + + 1.8 + 7.0 + + + + + 3.1.0 + 3.8.1 + 3.1.0 + 3.1.1 + 3.1.2 + 3.1.1 + 3.0.0-M1 + 3.0.0-M1 + 2.5.3 + 3.8.2 + 3.1.0 + 3.0.0-M3 + 0.8.4 + 2.7 + 3.0.0-M2 + 1.6 + + + 4.2 1.7.25 1.2.17 @@ -119,13 +82,8 @@ 4.12 2.15.0 - - - 2.0 - 1.3 - - 3.0.5.Final - 1.3.1.Final + 2.4.7.Final + 1.3.0.Final @@ -137,21 +95,32 @@ axon-configuration ${axon.version} - - - javax.enterprise - cdi-api - ${cdi-api.version} + org.axonframework + axon-test + ${axon.version} - - javax.transaction - javax.transaction-api - ${transaction.version} + javax + javaee-api + ${javaee-api.version} + + + + + + + + + + + + + + org.jboss.weld.se @@ -180,6 +149,16 @@ + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + org.slf4j jul-to-slf4j @@ -232,52 +211,141 @@ - - javax.enterprise - cdi-api - provided - + + + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + org.codehaus.mojo + versions-maven-plugin + ${maven-versions-plugin.version} + maven-clean-plugin - 3.0.0 + ${maven-clean-plugin.version} maven-install-plugin - 2.5.2 + ${maven-install-plugin.version} + + + maven-site-plugin + ${maven-site-plugin.version} + + + maven-resources-plugin + ${maven-resources-plugin.version} + + + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + maven-assembly-plugin + ${maven-assemply-plugin.version} + + + maven-release-plugin + ${maven-release-plugin.version} + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.jacoco + jacoco-maven-plugin + ${maven-jacoco-plugin.version} + + + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + maven-jar-plugin + ${maven-jar-plugin.version} + + + maven-source-plugin + ${maven-source-plugin.version} + + + maven-gpg-plugin + ${maven-gpg-version} + + - maven-resources-plugin - 3.0.2 + maven-enforcer-plugin + + + + enforce-maven + + enforce + + + + + ${java.version} + + + 3.5 + + + + + + + + + org.codehaus.mojo + versions-maven-plugin + - UTF-8 + false + - maven-deploy-plugin - 2.8.2 + maven-resources-plugin + + + UTF-8 + + maven-compiler-plugin - 3.5.1 + - 1.8 - 1.8 + ${java.version} + ${java.version} UTF-8 + - org.apache.maven.plugins maven-assembly-plugin - 2.6 + assembly @@ -285,18 +353,20 @@ + maven-release-plugin - 2.5.3 + forked-path true false + maven-surefire-plugin - 2.19.1 + **/*Test.java @@ -310,9 +380,10 @@ + maven-javadoc-plugin - 2.10.4 + attach-javadoc @@ -323,12 +394,13 @@ - -Xdoclint:none + none + maven-jar-plugin - 3.0.2 + @@ -337,9 +409,10 @@ + maven-source-plugin - 3.0.1 + attach-sources @@ -350,33 +423,68 @@ - - - maven-enforcer-plugin - 1.4.1 - - - enforce-java - deploy - - enforce - - - - - 1.8 - - - 3.5 - - - - - - + + + + release-sign-artifacts + + + performRelease + true + + + + + + maven-gpg-plugin + + + + sign-artifacts + verify + + + sign + + + + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + + + + + sonatype @@ -398,8 +506,8 @@ - scm:git:git://github.com/AxonFramework/extension-mongo.git - scm:git:git@github.com:AxonFramework/extension-mongo.git + scm:git:git://github.com/AxonFramework/extension-cdi.git + scm:git:git@github.com:AxonFramework/extension-cdi.git https://github.com/AxonFramework/extension-cdi HEAD