com.ottogroup.bi.asap.component.source.executor.SourceExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.ottogroup.bi.asap.component.source.executor.SourceExecutor.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.component.source.executor;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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

import com.ottogroup.bi.asap.component.source.Source;
import com.ottogroup.bi.asap.component.strategy.MessageWaitStrategy;
import com.ottogroup.bi.asap.component.strategy.YieldMessageWaitStrategy;
import com.ottogroup.bi.asap.exception.RequiredInputMissingException;
import com.ottogroup.bi.asap.exception.handler.ComponentExceptionHandler;
import com.ottogroup.bi.asap.exception.handler.ExecutorExceptionHandler;
import com.ottogroup.bi.asap.mailbox.Mailbox;
import com.ottogroup.bi.asap.message.StreamingDataMessage;

/**
 * Provides a runtime environment for {@link Source} instances
 * @author mnxfst
 * @since Dec 15, 2014
 * TODO create a executor service for this executor and the source
 * TODO testing 
 */
public class SourceExecutor implements Runnable {

    private static final Logger logger = Logger.getLogger(SourceExecutor.class);

    /** fixed thread pool suffice as we start only one thread */
    private final ExecutorService sourceExecutorService = Executors.newCachedThreadPool();
    private final Source source;
    private final Mailbox mailbox;
    private final Map<String, Mailbox> subscriberMailboxes = new HashMap<>();
    private final MessageWaitStrategy messageWaitStrategy;
    private final ExecutorExceptionHandler executorExceptionHandler;
    private final ComponentExceptionHandler componentExceptionHandler;
    private boolean running = false;

    /**
     * Initializes the executor using the provided input
     * @param source
     * @param messageWaitStrategy
     * @param executorExceptionHandler
     * @param componentExceptionHandler
     * @param mailbox mailbox provided to {@link Source} to receive {@link StreamingDataMessage messages}  
     * @throws RequiredInputMissingException
     */
    public SourceExecutor(final Source source, final MessageWaitStrategy messageWaitStrategy,
            final ExecutorExceptionHandler executorExceptionHandler,
            final ComponentExceptionHandler componentExceptionHandler, final Mailbox mailbox)
            throws RequiredInputMissingException {

        ///////////////////////////////////////////////////////////////////
        // validate input
        if (source == null)
            throw new RequiredInputMissingException("Missing required source instance");
        if (mailbox == null)
            throw new RequiredInputMissingException("Missing required mailbox");
        //
        ///////////////////////////////////////////////////////////////////

        if (messageWaitStrategy != null)
            this.messageWaitStrategy = messageWaitStrategy;
        else
            this.messageWaitStrategy = new YieldMessageWaitStrategy();

        this.source = source;
        this.running = true;
        this.executorExceptionHandler = executorExceptionHandler;
        this.componentExceptionHandler = componentExceptionHandler;
        this.mailbox = mailbox;

        this.source.setMailbox(mailbox);
        this.sourceExecutorService.submit(this.source);

        if (logger.isDebugEnabled())
            logger.debug("source executor initalized [id=" + source.getId() + ", running=" + running + ",mailbox="
                    + mailbox + "]");
    }

    /**
     * Adds the provided {@link Mailbox} as receiver of all {@link StreamingDataMessage} items received by the
     * underlying {@link Source}. The given identifier references the linked mailbox. 
     * @param adaptorId
     * @param messageSubscriber
     */
    public void subscribe(final String subscriberId, final Mailbox subscriberMailbox)
            throws RequiredInputMissingException {

        ///////////////////////////////////////////////////////////////////////////
        // validate input
        if (StringUtils.isBlank(subscriberId))
            throw new RequiredInputMissingException("Missing required subscriber identifier");
        if (subscriberMailbox == null)
            throw new RequiredInputMissingException("Missing required subscriber mailbox");
        //
        ///////////////////////////////////////////////////////////////////////////

        this.subscriberMailboxes.put(StringUtils.trim(StringUtils.lowerCase(subscriberId)), subscriberMailbox);

        if (logger.isDebugEnabled())
            logger.debug("subscribe[publisher=" + source.getId() + ", subscriber=" + subscriberId + ", mailbox="
                    + subscriberMailbox + "]");
    }

    /**
     * Un-subscribes the referenced subscriber from operator
     * @param subscriberId
     * @throws RequiredInputMissingException
     */
    public void unsubscribe(final String subscriberId) throws RequiredInputMissingException {

        ///////////////////////////////////////////////////////////////////////////
        // validate input
        if (StringUtils.isBlank(subscriberId))
            throw new RequiredInputMissingException("Missing required subscriber identifier");
        //
        ///////////////////////////////////////////////////////////////////////////

        this.subscriberMailboxes.remove(StringUtils.trim(StringUtils.lowerCase(subscriberId)));
    }

    /**
     * Returns true if the referenced component is a subscriber to this source
     * @param subscriberId
     * @return
     * @throws RequiredInputMissingException
     */
    public boolean isSubscriber(final String subscriberId) throws RequiredInputMissingException {

        ///////////////////////////////////////////////////////////////////////////
        // validate input
        if (StringUtils.isBlank(subscriberId))
            throw new RequiredInputMissingException("Missing required subscriber identifier");
        //
        ///////////////////////////////////////////////////////////////////////////

        return this.subscriberMailboxes.containsKey(StringUtils.trim(StringUtils.lowerCase(subscriberId)));
    }

    /**
     * @see java.lang.Runnable#run()
     */
    public void run() {
        // runs until it is stopped by calling shutdown()
        while (running) {

            // read next element from mailbox and provide it to the filter if
            // not null otherwise apply the assigned wait strategy and (hopefully)
            // free computational resources for next thread ;-)
            try {
                StreamingDataMessage message = this.mailbox.next();

                if (message != null) {
                    try {
                        for (final Mailbox subscriberMailbox : this.subscriberMailboxes.values()) {
                            subscriberMailbox.insert(message);
                        }
                    } catch (Exception e) {
                        if (this.componentExceptionHandler != null)
                            this.componentExceptionHandler.handleComponentException(e);
                        e.printStackTrace();
                    }
                } else {
                    try {
                        messageWaitStrategy.waitFor();
                    } catch (InterruptedException e) {
                        logger.info("Thread #" + Thread.currentThread().getId()
                                + " interrupted while waiting for next mailbox message. Reason: " + e.getMessage());
                    }
                }
            } catch (Exception e) {
                if (this.executorExceptionHandler != null)
                    this.executorExceptionHandler.handleExecutorException(e);
            }
        }

        try {
            this.source.shutdown();
        } catch (Exception e) {
            if (this.executorExceptionHandler != null)
                this.executorExceptionHandler.handleExecutorException(e);
        }
    }

    /**
     * Returns the identifier of the underlying {@link Source}
     * @return
     */
    public String getSourceId() {
        return this.source.getId();
    }

    /** 
     * Shuts down the executor
     */
    public void shutdown() {
        this.running = false;
    }

    /**
     * Returns the map holding all subscribed {@link Mailbox mailboxes}
     * @return
     */
    public Map<String, Mailbox> getSubscriberMailboxes() {
        return subscriberMailboxes;
    }

    /**
     * Indicates the running state of the executor
     * @return
     */
    public boolean isRunning() {
        return running;
    }

}