Package org.axonframework.contextsupport.spring

Source Code of org.axonframework.contextsupport.spring.DisruptorCommandBusBeanDefinitionParser$RepositoryFactoryBean

/*
* Copyright (c) 2010-2014. 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.contextsupport.spring;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.BusySpinWaitStrategy;
import com.lmax.disruptor.SleepingWaitStrategy;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
import org.axonframework.cache.WeakReferenceCache;
import org.axonframework.commandhandling.disruptor.DisruptorCommandBus;
import org.axonframework.commandhandling.disruptor.DisruptorConfiguration;
import org.axonframework.common.Assert;
import org.axonframework.eventsourcing.AggregateFactory;
import org.axonframework.eventsourcing.CompositeEventStreamDecorator;
import org.axonframework.eventsourcing.EventSourcedAggregateRoot;
import org.axonframework.eventsourcing.EventStreamDecorator;
import org.axonframework.eventsourcing.GenericAggregateFactory;
import org.axonframework.eventsourcing.SnapshotterTrigger;
import org.axonframework.repository.Repository;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition;

/**
* BeanDefinitionParser that parses <code>&lt;disruptor-command-bus&gt;</code> elements in the Spring context
*
* @author Allard Buijze
* @since 2.0
*/
public class DisruptorCommandBusBeanDefinitionParser extends AbstractBeanDefinitionParser {

    private static final String ATTRIBUTE_EVENT_STORE = "event-store";
    private static final String ATTRIBUTE_EVENT_BUS = "event-bus";
    private static final String ELEMENT_REPOSITORY = "repository";
    private static final String ATTRIBUTE_ID = "id";
    private static final String ATTRIBUTE_AGGREGATE_FACTORY = "aggregate-factory";
    private static final String ATTRIBUTE_AGGREGATE_TYPE = "aggregate-type";

    private static final String PROPERTY_WAIT_STRATEGY = "waitStrategy";
    private static final String ATTRIBUTE_WAIT_STRATEGY = "wait-strategy";
    private static final String ATTRIBUTE_CLAIM_STRATEGY = "claim-strategy";
    private static final String PROPERTY_PRODUCER_TYPE = "producerType";
    private static final String ATTRIBUTE_PRODUCER_TYPE = "producer-type";
    private static final String ELEMENT_REPOSITORIES = "repositories";

    private static final String EVENT_PROCESSORS_ELEMENT = "event-processors";
    private static final String SNAPSHOT_TRIGGER_ELEMENT = "snapshotter-trigger";

    private static final String EVENT_STREAM_DECORATORS_PROPERTY = "eventStreamDecorators";
    private static final String SNAPSHOTTER_TRIGGER_PROPERTY = "snapshotterTrigger";

    private static final String ATTRIBUTE_TRANSACTION_MANAGER = "transaction-manager";
    private static final String PROPERTY_TRANSACTION_MANAGER = "transactionManager";

    private static final Map<String, String> VALUE_PROPERTY_MAPPING = new HashMap<String, String>();
    private static final Map<String, String> REF_PROPERTY_MAPPING = new HashMap<String, String>();
    private static final Map<String, String> LIST_PROPERTY_MAPPING = new HashMap<String, String>();

    private final SnapshotterTriggerBeanDefinitionParser snapshotterTriggerParser =
            new SnapshotterTriggerBeanDefinitionParser();

    static {
        REF_PROPERTY_MAPPING.put("cache", "cache");
        REF_PROPERTY_MAPPING.put("executor", "executor");
        REF_PROPERTY_MAPPING.put("rollback-configuration", "rollbackConfiguration");
        REF_PROPERTY_MAPPING.put("serializer", "serializer");
        REF_PROPERTY_MAPPING.put("command-target-resolver", "commandTargetResolver");

        VALUE_PROPERTY_MAPPING.put("cooling-down-period", "coolingDownPeriod");
        VALUE_PROPERTY_MAPPING.put("invoker-threads", "invokerThreadCount");
        VALUE_PROPERTY_MAPPING.put("serializer-threads", "serializerThreadCount");
        VALUE_PROPERTY_MAPPING.put("publisher-threads", "publisherThreadCount");
        VALUE_PROPERTY_MAPPING.put("reschedule-commands-on-corrupt-state", "rescheduleCommandsOnCorruptState");
        VALUE_PROPERTY_MAPPING.put("serialized-representation", "serializedRepresentation");
        VALUE_PROPERTY_MAPPING.put("buffer-size", "bufferSize");

        LIST_PROPERTY_MAPPING.put("invoker-interceptors", "invokerInterceptors");
        LIST_PROPERTY_MAPPING.put("publisher-interceptors", "publisherInterceptors");
        LIST_PROPERTY_MAPPING.put("dispatcher-interceptors", "dispatchInterceptors");
    }

    @Override
    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        final BeanDefinition configurationDefinition = createConfiguration(element, parserContext);
        final AbstractBeanDefinition definition =
                genericBeanDefinition(DisruptorCommandBus.class)
                        .addConstructorArgReference(element.getAttribute(ATTRIBUTE_EVENT_STORE))
                        .addConstructorArgReference(element.getAttribute(ATTRIBUTE_EVENT_BUS))
                        .addConstructorArgValue(configurationDefinition)
                        .getBeanDefinition();
        Element repoElement = DomUtils.getChildElementByTagName(element, ELEMENT_REPOSITORIES);
        List<Element> repositories = DomUtils.getChildElementsByTagName(repoElement, ELEMENT_REPOSITORY);
        String id = super.resolveId(element, definition, parserContext);
        for (Element repository : repositories) {
            parseRepository(repository, id, parserContext,
                            configurationDefinition.getPropertyValues().getPropertyValue("cache"));
        }
        definition.setDestroyMethodName("stop");
        return definition;
    }

    private BeanDefinition createConfiguration(Element element, ParserContext parserContext) {
        final BeanDefinitionBuilder builder = genericBeanDefinition(DisruptorConfiguration.class);
        for (Map.Entry<String, String> entry : REF_PROPERTY_MAPPING.entrySet()) {
            if (element.hasAttribute(entry.getKey())) {
                builder.addPropertyReference(entry.getValue(), element.getAttribute(entry.getKey()));
            }
        }
        for (Map.Entry<String, String> entry : VALUE_PROPERTY_MAPPING.entrySet()) {
            if (element.hasAttribute(entry.getKey())) {
                builder.addPropertyValue(entry.getValue(), element.getAttribute(entry.getKey()));
            }
        }
        parseClaimStrategy(element, builder);
        parseProducerType(element, builder);
        parseWaitStrategy(element, builder);
        parseTransactionManager(element, builder);

        for (Map.Entry<String, String> entry : LIST_PROPERTY_MAPPING.entrySet()) {
            final Element interceptorsElement = DomUtils.getChildElementByTagName(element, entry.getKey());
            if (interceptorsElement != null) {
                builder.addPropertyValue(entry.getValue(),
                                         parserContext.getDelegate().parseListElement(interceptorsElement,
                                                                                      builder.getBeanDefinition())
                );
            }
        }
        return builder.getBeanDefinition();
    }

    private void parseTransactionManager(Element element, BeanDefinitionBuilder builder) {
        if (element.hasAttribute(ATTRIBUTE_TRANSACTION_MANAGER)) {
            final String txManagerId = element.getAttribute(ATTRIBUTE_TRANSACTION_MANAGER);
            builder.addPropertyValue(PROPERTY_TRANSACTION_MANAGER,
                                     BeanDefinitionBuilder.genericBeanDefinition(TransactionManagerFactoryBean.class)
                                                          .addPropertyReference(PROPERTY_TRANSACTION_MANAGER,
                                                                                txManagerId)
                                                          .getBeanDefinition()
            );
        }
    }

    @Deprecated
    private void parseClaimStrategy(Element element, BeanDefinitionBuilder builder) {
        final BeanDefinitionBuilder producerType = BeanDefinitionBuilder
                .genericBeanDefinition(ProducerTypeFactoryBean.class);
        if (element.hasAttribute(ATTRIBUTE_CLAIM_STRATEGY)) {
            producerType.addPropertyValue("type", element.getAttribute(ATTRIBUTE_CLAIM_STRATEGY));
        }
        builder.addPropertyValue(PROPERTY_PRODUCER_TYPE, producerType.getBeanDefinition());
    }

    private void parseProducerType(Element element, BeanDefinitionBuilder builder) {
        final BeanDefinitionBuilder producerType = BeanDefinitionBuilder
                .genericBeanDefinition(ProducerTypeFactoryBean.class);
        if (element.hasAttribute(ATTRIBUTE_PRODUCER_TYPE)) {
            producerType.addPropertyValue("type", element.getAttribute(ATTRIBUTE_PRODUCER_TYPE));
        }
        builder.addPropertyValue(PROPERTY_PRODUCER_TYPE, producerType.getBeanDefinition());
    }

    private void parseWaitStrategy(Element element, BeanDefinitionBuilder builder) {
        if (element.hasAttribute(ATTRIBUTE_WAIT_STRATEGY)) {
            String waitStrategy = element.getAttribute(ATTRIBUTE_WAIT_STRATEGY);
            builder.addPropertyValue(PROPERTY_WAIT_STRATEGY,
                                     BeanDefinitionBuilder.genericBeanDefinition(WaitStrategyFactoryBean.class)
                                                          .addConstructorArgValue(waitStrategy)
                                                          .getBeanDefinition()
            );
        }
    }

    private void parseRepository(Element repository, String commandBusId, ParserContext parserContext,
                                 PropertyValue aggregateCache) {
        String id = repository.getAttribute(ATTRIBUTE_ID);
        BeanDefinitionBuilder definitionBuilder =
                genericBeanDefinition(RepositoryFactoryBean.class)
                        .addPropertyReference("commandBus", commandBusId);
        if (repository.hasAttribute(ATTRIBUTE_AGGREGATE_FACTORY)) {
            definitionBuilder = definitionBuilder
                    .addPropertyReference("aggregateFactory", repository.getAttribute(ATTRIBUTE_AGGREGATE_FACTORY));
        } else {
            final String aggregateType = repository.getAttribute(ATTRIBUTE_AGGREGATE_TYPE);
            Assert.notEmpty(aggregateType, "Either one of 'aggregate-type' or 'aggregate-factory' attributes must be "
                    + "set on repository elements in <disruptor-command-bus>");
            definitionBuilder = definitionBuilder
                    .addPropertyValue("aggregateFactory", genericBeanDefinition(GenericAggregateFactory.class)
                            .addConstructorArgValue(aggregateType)
                            .addConstructorArgValue(SpringContextParameterResolverFactoryBuilder
                                                            .getBeanReference(parserContext.getRegistry()))
                            .getBeanDefinition());
        }

        Element processorsElement = DomUtils.getChildElementByTagName(repository, EVENT_PROCESSORS_ELEMENT);

        Element snapshotTriggerElement = DomUtils.getChildElementByTagName(repository, SNAPSHOT_TRIGGER_ELEMENT);
        if (snapshotTriggerElement != null) {
            BeanDefinition triggerDefinition = snapshotterTriggerParser.parse(snapshotTriggerElement, parserContext);
            if (aggregateCache != null) {
                triggerDefinition.getPropertyValues().add("aggregateCache", aggregateCache.getValue());
            } else {
                // the DisruptorCommandBus uses an internal cache. Not defining any cache on the snapshotter trigger
                // would lead to undesired side-effects (mainly missing triggers).
                triggerDefinition.getPropertyValues().add("aggregateCache", BeanDefinitionBuilder
                        .genericBeanDefinition(WeakReferenceCache.class).getBeanDefinition());
            }
            definitionBuilder = definitionBuilder.addPropertyValue(SNAPSHOTTER_TRIGGER_PROPERTY, triggerDefinition);
        }

        final AbstractBeanDefinition definition = definitionBuilder.getBeanDefinition();
        if (processorsElement != null) {
            //noinspection unchecked
            List<Object> processorsList = parserContext.getDelegate().parseListElement(processorsElement,
                                                                                       definition);
            if (!processorsList.isEmpty()) {
                definition.getPropertyValues().add(EVENT_STREAM_DECORATORS_PROPERTY, processorsList);
            }
        }
        parserContext.getRegistry().registerBeanDefinition(id, definition);
    }

    @Override
    protected boolean shouldGenerateIdAsFallback() {
        return true;
    }

    /**
     * Factory bean that creates a ProducerType instance.
     */
    private static final class ProducerTypeFactoryBean implements FactoryBean<ProducerType>, InitializingBean {

        private ProducerType producerType;
        private String type;

        @Override
        public ProducerType getObject() throws Exception {
            return producerType;
        }

        @Override
        public Class<?> getObjectType() {
            return ProducerType.class;
        }

        @Override
        public boolean isSingleton() {
            return true;
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            if ("single-threaded".equals(type)) {
                producerType = ProducerType.SINGLE;
            } else {
                producerType = ProducerType.MULTI;
            }
        }

        /**
         * Sets the name of the producer type to use.
         *
         * @param type the name of the producer type to use
         */
        @SuppressWarnings("UnusedDeclaration")
        public void setType(String type) {
            Assert.isTrue("single-threaded".equals(type) || "multi-threaded".equals(type),
                          "The given value for producer type (" + type
                                  + ") is not valid. It must either be 'single-threaded' or 'multi-threaded'."
            );
            this.type = type;
        }
    }

    private static final class WaitStrategyFactoryBean implements FactoryBean<WaitStrategy> {

        private final WaitStrategy waitStrategy;

        @SuppressWarnings("UnusedDeclaration")
        private WaitStrategyFactoryBean(String strategyName) {
            if ("busy-spin".equals(strategyName)) {
                waitStrategy = new BusySpinWaitStrategy();
            } else if ("yield".equals(strategyName)) {
                waitStrategy = new YieldingWaitStrategy();
            } else if ("sleep".equals(strategyName)) {
                waitStrategy = new SleepingWaitStrategy();
            } else if ("block".equals(strategyName)) {
                waitStrategy = new BlockingWaitStrategy();
            } else {
                throw new IllegalArgumentException("WaitStrategy is not one of the allowed values: "
                                                           + "busy-spin, yield, sleep or block.");
            }
        }

        @Override
        public WaitStrategy getObject() throws Exception {
            return waitStrategy;
        }

        @Override
        public Class<?> getObjectType() {
            return WaitStrategy.class;
        }

        @Override
        public boolean isSingleton() {
            return true;
        }
    }

    /**
     * FactoryBean to create repository instances for use with the DisruptorCommandBus
     */
    public static class RepositoryFactoryBean implements FactoryBean<Repository> {

        private DisruptorCommandBus commandBus;
        private List<EventStreamDecorator> eventStreamDecorators = new ArrayList<EventStreamDecorator>();
        private AggregateFactory<? extends EventSourcedAggregateRoot> factory;

        @Override
        public Repository getObject() throws Exception {
            if (eventStreamDecorators.isEmpty()) {
                return commandBus.createRepository(factory);
            }
            return commandBus.createRepository(factory, new CompositeEventStreamDecorator(eventStreamDecorators));
        }

        @Override
        public Class<?> getObjectType() {
            return Repository.class;
        }

        @Override
        public boolean isSingleton() {
            return true;
        }

        /**
         * The DisruptorCommandBus instance to create the repository from. This is a required property, but cannot be
         * set using constructor injection, as Spring will see it as a circular dependency.
         *
         * @param commandBus DisruptorCommandBus instance to create the repository from
         */
        @Required
        public void setCommandBus(DisruptorCommandBus commandBus) {
            this.commandBus = commandBus;
        }

        /**
         * Sets the aggregate factory used to create instances for the repository to create. This is a required
         * property, but cannot be set using constructor injection, as Spring will see it as a circular dependency.
         *
         * @param factory the aggregate factory used to create instances for the repository to create
         */
        @Required
        public void setAggregateFactory(AggregateFactory<? extends EventSourcedAggregateRoot> factory) {
            this.factory = factory;
        }

        /**
         * Sets the (additional) decorators to use when loading and storing events from/to the Event Store.
         *
         * @param decorators the decorators to decorate event streams with
         */
        @SuppressWarnings("UnusedDeclaration")
        public void setEventStreamDecorators(List<EventStreamDecorator> decorators) {
            eventStreamDecorators.addAll(decorators);
        }

        /**
         * The snapshotter trigger instance that will decide when to trigger a snapshot
         *
         * @param snapshotterTrigger The trigger to configure on the DisruptorCommandBus
         */
        @SuppressWarnings("UnusedDeclaration")
        public void setSnapshotterTrigger(SnapshotterTrigger snapshotterTrigger) {
            eventStreamDecorators.add(snapshotterTrigger);
        }
    }
}
TOP

Related Classes of org.axonframework.contextsupport.spring.DisruptorCommandBusBeanDefinitionParser$RepositoryFactoryBean

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.