/* * Copyright (c) 2010-2016. Axon Framework * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.axonframework.config; import org.axonframework.commandhandling.AggregateAnnotationCommandHandler; import org.axonframework.commandhandling.AnnotationCommandTargetResolver; import org.axonframework.commandhandling.CommandTargetResolver; import org.axonframework.commandhandling.disruptor.DisruptorCommandBus; import org.axonframework.commandhandling.model.GenericJpaRepository; import org.axonframework.commandhandling.model.Repository; import org.axonframework.common.Assert; import org.axonframework.common.Registration; import org.axonframework.common.jpa.EntityManagerProvider; import org.axonframework.eventsourcing.*; import org.axonframework.eventsourcing.eventstore.EventStore; import java.util.ArrayList; import java.util.List; import java.util.function.Function; /** * Axon Configuration API extension that allows the definition of an Aggregate. This component will automatically * setup all components required for the Aggregate to operate. * * @param <A> The type of Aggregate configured */ public class AggregateConfigurer<A> implements AggregateConfiguration<A> { private final Class<A> aggregate; private final Component<AggregateAnnotationCommandHandler> commandHandler; private final Component<Repository<A>> repository; private final Component<AggregateFactory<A>> aggregateFactory; private final Component<SnapshotTriggerDefinition> snapshotTriggerDefinition; private final Component<CommandTargetResolver> commandTargetResolver; private Configuration parent; private final List<Registration> registrations = new ArrayList<>(); /** * Creates a default Configuration for an aggregate of the given {@code aggregateType}. This required either a * Repository to be configured using {@link #configureRepository(Function)}, or that the Global Configuration * contains an Event Store and the Aggregate support Event Sourcing. * * @param aggregateType The type of Aggregate to configure * @param <A> The type of Aggregate to configure * @return An AggregateConfigurer instance for further configuration of the Aggregate */ public static <A> AggregateConfigurer<A> defaultConfiguration(Class<A> aggregateType) { return new AggregateConfigurer<>(aggregateType); } /** * Creates a Configuration for an aggregate of given {@code aggregateType}, which is mapped to a relational * database using an EntityManager provided by given {@code entityManagerProvider}. The given {@code aggregateType} * is expected to be a proper JPA Entity. * * @param aggregateType The type of Aggregate to configure * @param entityManagerProvider The provider for Axon to retrieve the EntityManager from * @param <A> The type of Aggregate to configure * @return An AggregateConfigurer instance for further configuration of the Aggregate */ public static <A> AggregateConfigurer<A> jpaMappedConfiguration(Class<A> aggregateType, EntityManagerProvider entityManagerProvider) { return new AggregateConfigurer<>(aggregateType).configureRepository( c -> new GenericJpaRepository<>(entityManagerProvider, aggregateType, c.eventBus())); } /** * Creates a default configuration as described in {@link #defaultConfiguration(Class)}. This constructor is * available for subclasses that provide additional configuration possibilities. * * @param aggregate The type of aggregate to configure */ protected AggregateConfigurer(Class<A> aggregate) { this.aggregate = aggregate; commandTargetResolver = new Component<>(() -> parent, name("commandTargetResolver"), c -> new AnnotationCommandTargetResolver()); snapshotTriggerDefinition = new Component<>(() -> parent, name("snapshotTriggerDefinition"), c -> NoSnapshotTriggerDefinition.INSTANCE); aggregateFactory = new Component<>(() -> parent, name("aggregateFactory"), c -> new GenericAggregateFactory<>(aggregate)); repository = new Component<>(() -> parent, "Repository<" + aggregate.getSimpleName() + ">", c -> { Assert.state(c.eventBus() instanceof EventStore, () -> "Default configuration requires the use of event sourcing. Either configure an Event " + "Store to use, or configure a specific repository implementation for " + aggregate.toString()); if (c.commandBus() instanceof DisruptorCommandBus) { return ((DisruptorCommandBus) c.commandBus()) .createRepository(aggregateFactory.get(), c.parameterResolverFactory()); } return new EventSourcingRepository<>(aggregateFactory.get(), c.eventStore(), c.parameterResolverFactory(), snapshotTriggerDefinition.get()); }); commandHandler = new Component<>(() -> parent, "aggregateCommandHandler<" + aggregate.getSimpleName() + ">", c -> new AggregateAnnotationCommandHandler<>(aggregate, repository.get(), commandTargetResolver.get(), c.parameterResolverFactory())); } private String name(String prefix) { return prefix + "<" + aggregate.getSimpleName() + ">"; } /** * Defines the repository to use to load and store Aggregates of this type. The builder function receives the * global configuration object from which it can retrieve components the repository depends on. * * @param repositoryBuilder The builder function for the repository * @return this configurer instance for chaining */ public AggregateConfigurer<A> configureRepository(Function<Configuration, Repository<A>> repositoryBuilder) { repository.update(repositoryBuilder); return this; } /** * Defines the factory to use to to create new Aggregates instances of the type under configuration. * * @param aggregateFactoryBuilder The builder function for the AggregateFactory * @return this configurer instance for chaining */ public AggregateConfigurer<A> configureAggregateFactory( Function<Configuration, AggregateFactory<A>> aggregateFactoryBuilder) { aggregateFactory.update(aggregateFactoryBuilder); return this; } /** * Defines the AggregateAnnotationCommandHandler instance to use. * * @param aggregateCommandHandlerBuilder The builder function for the AggregateCommandHandler * @return this configurer instance for chaining */ public AggregateConfigurer<A> configureCommandHandler( Function<Configuration, AggregateAnnotationCommandHandler> aggregateCommandHandlerBuilder) { commandHandler.update(aggregateCommandHandlerBuilder); return this; } /** * Defines the CommandTargetResolver to use for the Aggregate type under configuration. The CommandTargetResolver * defines which Aggregate instance must be loaded to handle a specific Command. * * @param commandTargetResolverBuilder the builder function for the CommandTargetResolver. * @return this configurer instance for chaining */ public AggregateConfigurer<A> configureCommandTargetResolver( Function<Configuration, CommandTargetResolver> commandTargetResolverBuilder) { commandTargetResolver.update(commandTargetResolverBuilder); return this; } @Override public void initialize(Configuration parent) { this.parent = parent; } @Override public void start() { registrations.add(commandHandler.get().subscribe(parent.commandBus())); } @Override public void shutdown() { registrations.forEach(Registration::cancel); registrations.clear(); } @Override public Repository<A> repository() { return repository.get(); } @Override public Class<A> aggregateType() { return aggregate; } }