com.ottogroup.bi.asap.pipeline.MicroPipelineFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.ottogroup.bi.asap.pipeline.MicroPipelineFactory.java

Source

/**
 * Copyright 2014 Otto (GmbH & Co KG)
 *
 * 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 com.ottogroup.bi.asap.pipeline;

import java.util.Properties;
import java.util.concurrent.ExecutorService;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.ottogroup.bi.asap.component.ComponentConfiguration;
import com.ottogroup.bi.asap.component.ComponentType;
import com.ottogroup.bi.asap.component.emitter.Emitter;
import com.ottogroup.bi.asap.component.emitter.executor.EmitterExecutor;
import com.ottogroup.bi.asap.component.operator.DelayedResponseOperator;
import com.ottogroup.bi.asap.component.operator.DirectResponseOperator;
import com.ottogroup.bi.asap.component.operator.executor.DelayedResponseOperatorExecutor;
import com.ottogroup.bi.asap.component.operator.executor.DirectResponseOperatorExecutor;
import com.ottogroup.bi.asap.component.source.Source;
import com.ottogroup.bi.asap.component.source.executor.SourceExecutor;
import com.ottogroup.bi.asap.component.strategy.DelayedResponseWaitStrategy;
import com.ottogroup.bi.asap.component.strategy.MessageWaitStrategy;
import com.ottogroup.bi.asap.component.strategy.YieldMessageWaitStrategy;
import com.ottogroup.bi.asap.component.strategy.config.DelayedResponseWaitStrategyConfiguration;
import com.ottogroup.bi.asap.component.strategy.config.MessageWaitStrategyConfiguration;
import com.ottogroup.bi.asap.exception.ComponentAlreadySubmittedException;
import com.ottogroup.bi.asap.exception.ComponentInstantiationFailedException;
import com.ottogroup.bi.asap.exception.IllegalComponentSubscriptionException;
import com.ottogroup.bi.asap.exception.RequiredInputMissingException;
import com.ottogroup.bi.asap.exception.UnknownComponentException;
import com.ottogroup.bi.asap.mailbox.DefaultMailbox;
import com.ottogroup.bi.asap.mailbox.Mailbox;
import com.ottogroup.bi.asap.mailbox.config.MailboxConfiguration;
import com.ottogroup.bi.asap.repository.ComponentRepository;

/**
 * Implements a factory for creating {@link MicroPipeline} instances from a given {@link MicroPipelineConfiguration}
 * @author mnxfst
 * @since Dec 16, 2014
 * TODO more strategies and more flexible
 * TODO activate exception handling
 */
public class MicroPipelineFactory {

    /** our faithful logging service ;-) */
    private static final Logger logger = Logger.getLogger(MicroPipelineFactory.class);

    /** reference to component repository */
    private final ComponentRepository componentRepository;

    /**
     * Initializes the factory instance using the provided input
     * @param componentRepository
     */
    public MicroPipelineFactory(final ComponentRepository componentRepository) {
        this.componentRepository = componentRepository;
    }

    /**
     * Instantiates a {@link MicroPipeline} according to provided {@link MicroPipelineConfiguration}
     * @param configuration
     * @param executorService
     * @return
     * @throws RequiredInputMissingException
     * @throws ComponentAlreadySubmittedException
     * @throws ComponentInstantiationFailedException
     * @throws UnknownComponentException
     * @throws IllegalComponentSubscriptionException 
     */
    public MicroPipeline instantiate(final MicroPipelineConfiguration configuration,
            final ExecutorService executorService) throws RequiredInputMissingException,
            ComponentAlreadySubmittedException, ComponentInstantiationFailedException, UnknownComponentException,
            IllegalComponentSubscriptionException {

        /////////////////////////////////////////////////////////////
        // validate
        if (configuration == null)
            throw new RequiredInputMissingException("Missing required configuration");
        if (StringUtils.isBlank(configuration.getId()))
            throw new RequiredInputMissingException("Missing required micro pipeline identifier");
        if (configuration.getComponents() == null || configuration.getComponents().isEmpty())
            throw new RequiredInputMissingException("Missing required components");
        //
        /////////////////////////////////////////////////////////////

        MicroPipeline mp = new MicroPipeline(configuration.getId(), configuration.getDescription(),
                executorService);

        /////////////////////////////////////////////////////////////
        // instantiate components
        for (final ComponentConfiguration cc : configuration.getComponents()) {
            if (cc != null) {

                try {
                    switch (cc.getType()) {
                    case DELAYED_RESPONSE_OPERATOR: {
                        mp.submitOperatorExecutor(instantiateDelayedResponseOperatorExecutor(cc));
                        break;
                    }
                    case DIRECT_RESPONSE_OPERATOR: {
                        mp.submitOperatorExecutor(instantiateDirectResponseOperatorExecutor(cc));
                        break;
                    }
                    case EMITTER: {
                        mp.submitEmitterExecutor(instantiateEmitterExecutor(cc));
                        break;
                    }
                    case SOURCE: {
                        mp.submitSourceExecutor(instantiateSourceExecutor(cc));
                        break;
                    }
                    default: {
                        throw new IllegalStateException("Invalid component type: '" + cc.getType() + "'");
                    }
                    }
                } catch (Throwable e) {
                    logger.error("Failed to instantiate micro pipeline [id=" + configuration.getId()
                            + ", componentId=" + cc.getId() + "]. Error: " + e.getMessage());
                    mp.shutdown();
                }
            }
        }
        // 
        /////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////
        // subscribe components to publishers
        for (final ComponentConfiguration cc : configuration.getComponents()) {
            if (cc != null && cc.getType() != ComponentType.SOURCE) {
                try {
                    if (cc.getSubscriptions() == null || cc.getSubscriptions().isEmpty())
                        throw new RequiredInputMissingException(
                                "Missing required subscriptions for component '" + cc.getId() + "'");

                    for (String publisherId : cc.getSubscriptions()) {
                        if (StringUtils.isBlank(publisherId))
                            throw new RequiredInputMissingException(
                                    "Empty value found in subscriptions list for component '" + cc.getId() + "'");

                        mp.subscribe(cc.getId(), publisherId);
                    }
                } catch (Throwable e) {
                    logger.error("Failed to subscribe micro pipeline components [id=" + configuration.getId()
                            + "]. Error: " + e.getMessage());
                    mp.shutdown();
                }
            }
        }
        //      
        /////////////////////////////////////////////////////////////

        return mp;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // EXECUTOR INSTANTIATION

    /**
     * Instantiates the {@link SourceExecutor} according to provided configuration
     * @param cfg expected not to be null
     * @return
     * @throws RequiredInputMissingException
     * @throws UnknownComponentException 
     * @throws ComponentInstantiationFailedException 
     */
    protected SourceExecutor instantiateSourceExecutor(final ComponentConfiguration cfg)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {

        Mailbox mailbox = getMailbox(cfg.getMailbox());
        MessageWaitStrategy messageWaitStrategy = getMessageWaitStrategy(cfg.getMessageWaitStrategy(), mailbox);

        if (logger.isDebugEnabled())
            logger.debug("source message wait strategy[id=" + messageWaitStrategy.getId() + ", type="
                    + messageWaitStrategy.getClass().getName() + "]");

        // TODO get exception handler
        // TODO get operator exception handler
        return new SourceExecutor((Source) this.componentRepository.newInstance(cfg.getId(), cfg.getName(),
                cfg.getVersion(), cfg.getSettings()), messageWaitStrategy, null, null, mailbox);
    }

    /**
     * Instantiates the {@link DelayedResponseOperatorExecutor} according to provided configuration
     * @param cfg expected not to be null
     * @return
     * @throws RequiredInputMissingException
     * @throws ComponentInstantiationFailedException
     * @throws UnknownComponentException
     */
    protected DelayedResponseOperatorExecutor instantiateDelayedResponseOperatorExecutor(
            final ComponentConfiguration cfg)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {

        Mailbox mailbox = getMailbox(cfg.getMailbox());
        MessageWaitStrategy messageWaitStrategy = getMessageWaitStrategy(cfg.getMessageWaitStrategy(), mailbox);

        if (logger.isDebugEnabled())
            logger.debug("delayed response operator message wait strategy[id=" + messageWaitStrategy.getId()
                    + ", type=" + messageWaitStrategy.getClass().getName() + "]");

        // TODO get exception handler
        // TODO get operator exception handler
        return new DelayedResponseOperatorExecutor(
                (DelayedResponseOperator) this.componentRepository.newInstance(cfg.getId(), cfg.getName(),
                        cfg.getVersion(), cfg.getSettings()),
                getDelayedResponseWaitStrategy(cfg.getDelayedResponseWaitStrategy()), messageWaitStrategy, null,
                null, mailbox);
    }

    /**
     * Instantiates the {@link DirectResponseOperatorExecutor} according to provided configuration
     * @param cfg expected not to be null
     * @return
     * @throws RequiredInputMissingException
     * @throws ComponentInstantiationFailedException
     * @throws UnknownComponentException
     */
    protected DirectResponseOperatorExecutor instantiateDirectResponseOperatorExecutor(
            final ComponentConfiguration cfg)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {

        Mailbox mailbox = getMailbox(cfg.getMailbox());
        MessageWaitStrategy messageWaitStrategy = getMessageWaitStrategy(cfg.getMessageWaitStrategy(), mailbox);

        if (logger.isDebugEnabled())
            logger.debug("direct response operator message wait strategy[id=" + messageWaitStrategy.getId()
                    + ", type=" + messageWaitStrategy.getClass().getName() + "]");

        // TODO get exception handler
        // TODO get operator exception handler
        return new DirectResponseOperatorExecutor((DirectResponseOperator) this.componentRepository
                .newInstance(cfg.getId(), cfg.getName(), cfg.getVersion(), cfg.getSettings()), messageWaitStrategy,
                null, null, mailbox);
    }

    /**
     * Instantiates the {@link EmitterExecutor} according to provided configuration
     * @param cfg
     * @return
     * @throws RequiredInputMissingException
     * @throws ComponentInstantiationFailedException
     * @throws UnknownComponentException
     */
    protected EmitterExecutor instantiateEmitterExecutor(final ComponentConfiguration cfg)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {
        Mailbox mailbox = getMailbox(cfg.getMailbox());
        MessageWaitStrategy messageWaitStrategy = getMessageWaitStrategy(cfg.getMessageWaitStrategy(), mailbox);

        if (logger.isDebugEnabled())
            logger.debug("emitter message wait strategy[id=" + messageWaitStrategy.getId() + ", type="
                    + messageWaitStrategy.getClass().getName() + "]");

        // TODO get exception handler
        // TODO get operator exception handler
        return new EmitterExecutor((Emitter) this.componentRepository.newInstance(cfg.getId(), cfg.getName(),
                cfg.getVersion(), cfg.getSettings()), messageWaitStrategy, null, null, mailbox);
    }

    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // STRATEGIES 

    /**
     * Returns the {@link MessageWaitStrategy} according to the provided configuration
     * @param configuration
     * @return
     * @throws RequiredInputMissingException
     * @throws UnknownComponentException 
     * @throws ComponentInstantiationFailedException 
     */
    protected MessageWaitStrategy getMessageWaitStrategy(final MessageWaitStrategyConfiguration configuration,
            final Mailbox mailbox)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {

        /////////////////////////////////////////////////////////////
        // validate mailbox
        if (mailbox == null)
            throw new RequiredInputMissingException("Missing required mailbox reference");
        //
        /////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////
        // if no configuration is provided, use default setting - which is by far the worst solution ;-)
        if (configuration == null) {
            YieldMessageWaitStrategy strategy = new YieldMessageWaitStrategy();
            strategy.setId("waitStrategy-#" + System.currentTimeMillis());
            strategy.setMailbox(mailbox);
            return strategy;
        }
        //
        /////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////
        // validate input
        if (StringUtils.isBlank(configuration.getId()))
            throw new RequiredInputMissingException("Missing required strategy id");
        if (StringUtils.isBlank(configuration.getName()))
            throw new RequiredInputMissingException("Missing required strategy name");
        if (StringUtils.isBlank(configuration.getVersion()))
            throw new RequiredInputMissingException("Missing required strategy version");
        //
        /////////////////////////////////////////////////////////////

        try {
            return (MessageWaitStrategy) componentRepository.newInstance(configuration.getId(),
                    configuration.getName(), configuration.getVersion(), configuration.getSettings());
        } catch (ClassCastException e) {
            throw new ComponentInstantiationFailedException(
                    "Failed to convert retrieved instance to mailbox representation. Error: " + e.getMessage());
        }
    }

    /**
     * Returns the {@link DelayedResponseWaitStrategy} according to the provided configuration
     * @param configuration
     * @return
     * @throws RequiredInputMissingException
     * @throws ComponentInstantiationFailedException 
     * @throws UnknownComponentException 
     */
    protected DelayedResponseWaitStrategy getDelayedResponseWaitStrategy(
            final DelayedResponseWaitStrategyConfiguration configuration)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {

        /////////////////////////////////////////////////////////////
        // validate input
        if (configuration == null)
            throw new RequiredInputMissingException("Missing required configuration");
        if (StringUtils.isBlank(configuration.getId()))
            throw new RequiredInputMissingException("Missing required strategy id");
        if (StringUtils.isBlank(configuration.getName()))
            throw new RequiredInputMissingException("Missing required strategy name");
        if (StringUtils.isBlank(configuration.getVersion()))
            throw new RequiredInputMissingException("Missing required strategy version");
        //
        /////////////////////////////////////////////////////////////

        try {
            return (DelayedResponseWaitStrategy) componentRepository.newInstance(configuration.getId(),
                    configuration.getName(), configuration.getVersion(), configuration.getSettings());
        } catch (ClassCastException e) {
            throw new ComponentInstantiationFailedException(
                    "Failed to convert retrieved instance to mailbox representation. Error: " + e.getMessage());
        }
    }

    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Returns the {@link Mailbox} implementation according to the provided configuration
     * @param configuration
     * @return
     * @throws RequiredInputMissingException
     * @throws UnknownComponentException 
     * @throws ComponentInstantiationFailedException 
     */
    protected Mailbox getMailbox(final MailboxConfiguration configuration)
            throws RequiredInputMissingException, ComponentInstantiationFailedException, UnknownComponentException {

        /////////////////////////////////////////////////////////////
        // return default mailbox if no configuration is provided
        if (configuration == null) {
            return getDefaultMailbox();
        }
        //
        /////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////
        // validate input
        if (StringUtils.isBlank(configuration.getId()))
            throw new RequiredInputMissingException("Missing required mailbox id");
        if (StringUtils.isBlank(configuration.getName()))
            throw new RequiredInputMissingException("Missing required mailbox name");
        if (StringUtils.isBlank(configuration.getVersion()))
            throw new RequiredInputMissingException("Missing required mailbox version");
        //
        /////////////////////////////////////////////////////////////

        try {
            return (Mailbox) componentRepository.newInstance(configuration.getId(), configuration.getName(),
                    configuration.getVersion(), configuration.getSettings());
        } catch (ClassCastException e) {
            throw new ComponentInstantiationFailedException(
                    "Failed to convert retrieved instance to mailbox representation. Error: " + e.getMessage());
        }
    }

    /**
     * Returns the default mailbox
     * @return
     * @throws RequiredInputMissingException
     */
    protected Mailbox getDefaultMailbox() throws RequiredInputMissingException {
        Properties properties = new Properties();
        properties.put(DefaultMailbox.CFG_MAILBOX_SIZE, String.valueOf(1000));
        Mailbox mailbox = new DefaultMailbox();
        mailbox.init(properties);
        return mailbox;
    }
}