com.aol.advertising.qiao.agent.DataFunnel.java Source code

Java tutorial

Introduction

Here is the source code for com.aol.advertising.qiao.agent.DataFunnel.java

Source

/****************************************************************************
 * Copyright (c) 2015 AOL Inc.
 * @author:     ytung05
 *
 * 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.aol.advertising.qiao.agent;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;

import com.aol.advertising.qiao.config.ConfigConstants;
import com.aol.advertising.qiao.emitter.IDataEmitterContainer;
import com.aol.advertising.qiao.injector.IDataInjector;
import com.aol.advertising.qiao.management.metrics.IStatisticsStore;
import com.aol.advertising.qiao.management.metrics.PubStats;
import com.aol.advertising.qiao.management.metrics.StatsCalculator;
import com.aol.advertising.qiao.management.metrics.StatsCollector;
import com.aol.advertising.qiao.management.metrics.StatsEnum;
import com.aol.advertising.qiao.util.CommonUtils;
import com.aol.advertising.qiao.util.ContextUtils;

/**
 * DataFunnel transports data from an injector to emitters.
 */
@ManagedResource
public class DataFunnel implements IFunnel, Runnable {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private String id;
    private IDataInjector injector;
    private IDataEmitterContainer emitterContainer;
    private volatile boolean running = false;
    private AtomicBoolean isStarted = new AtomicBoolean(false);
    private AtomicBoolean isSuspended = new AtomicBoolean(false);
    private boolean autoStart = true;
    protected int targetLimitedRate = 0;

    private String dataPipeClassname;
    private IDataPipe dataPipe;
    private int dataPipeCapacity;
    private int emitterThreadCount = 1;
    private StatsCollector statsCollector;
    private StatsCalculator statsCalculator;
    private IStatisticsStore statsStore;

    //
    private Map<String, PubStats> counterKeys = new LinkedHashMap<String, PubStats>();

    private ScheduledExecutorService scheduler;
    private int initDelaySecs = 10;
    private int intervalSecs = 5;
    private ScheduledFuture<?> schedFuture;

    @Override
    public void init() throws Exception {

        if (statsStore != null) {
            statsStore.setId(id);
            statsStore.init();
        }

        dataPipe = createDataPipe();
        injector.setDataPipe(dataPipe);
        emitterContainer.setDataPipe(dataPipe);
        emitterContainer.setEmitterThreadCount(emitterThreadCount);
        emitterContainer.setFunnelId(id); // pass funnel id
        emitterContainer.setRateLimit(targetLimitedRate);
        emitterContainer.init();
        injector.init();

        dataPipe.init();

        _registerStatsCollector();
        _registerStatsCalculator();

        scheduler = CommonUtils.createScheduledExecutorService(1,
                CommonUtils.resolveThreadName("DataFunnel_" + id));

        logger.info(this.getClass().getSimpleName() + " initialized");

    }

    private IDataPipe createDataPipe() throws Exception {
        if (dataPipeClassname == null)
            dataPipeClassname = ConfigConstants.DEFAULT_FUNNEL_DATAPIPE_CLASSNAME;

        IDataPipe pipe = (IDataPipe) ContextUtils.getOrCreateBean(dataPipeClassname);
        pipe.setCapacity(dataPipeCapacity);

        return pipe;

    }

    private void _registerStatsCollector() {
        if (statsCollector != null) {
            statsCollector.register(new Callable<Void>() {
                @Override
                public Void call() {
                    long inputs = dataPipe.getAndResetNumWrites();
                    if (inputs > 0) {
                        statsStore.incr(StatsEnum.EVENT_INPUT_COUNT.value(), inputs);
                    }

                    long outputs = dataPipe.getAndResetNumReads();
                    if (outputs > 0) {
                        statsStore.incr(StatsEnum.PROCESSED_COUNT.value(), outputs);
                    }

                    return null;
                }

            });
        }

    }

    private void _registerStatsCalculator() {
        if (statsCalculator != null) {
            initCounters();
            statsCalculator.register(statsCalculator.new CalcCallable(statsStore, counterKeys));
        }

    }

    private void initCounters() {
        if (counterKeys.size() == 0) {
            PubStats pstats = new PubStats(StatsEnum.EVENT_INPUT_COUNT.value(), false, true, true, false);
            counterKeys.put(pstats.getMetric(), pstats);
            pstats = new PubStats(StatsEnum.EVENT_OUTPUT_COUNT.value(), false, true, true, false);
            counterKeys.put(pstats.getMetric(), pstats);
            pstats = new PubStats(StatsEnum.PROCESSED_COUNT.value(), false, true, true, false);
            counterKeys.put(pstats.getMetric(), pstats);

            pstats = new PubStats(StatsEnum.DATAPIPE_QUEUELEN.value(), false, false, false, true);
            pstats.setGauge(new Callable<Integer>() {

                @Override
                public Integer call() throws Exception {
                    return dataPipe.size();
                }
            });
            counterKeys.put(pstats.getMetric(), pstats);
        }

    }

    @Override
    public void start() throws Exception {
        if (autoStart && isStarted.compareAndSet(false, true)) {
            _start();
        }

    }

    private void _start() throws Exception {
        dataPipe.start();

        emitterContainer.start();
        injector.start();

        running = true;

        schedFuture = scheduler.scheduleAtFixedRate(this, this.initDelaySecs, this.intervalSecs, TimeUnit.SECONDS);

        logger.info(this.getClass().getSimpleName() + " started");

    }

    @ManagedOperation
    public void manualStart() throws Exception {
        if (!autoStart && isStarted.compareAndSet(false, true)) {
            _start();
        }

    }

    @Override
    public void close() {
        running = false;

        if (schedFuture != null)
            schedFuture.cancel(false);
        if (scheduler != null)
            scheduler.shutdown();
        if (injector != null)
            injector.shutdown();
        if (emitterContainer != null)
            emitterContainer.shutdown();

        logger.info(this.getClass().getSimpleName() + " closed");
    }

    @Override
    public void setDataInjector(IDataInjector src) {
        this.injector = src;
    }

    @Override
    public void setDataEmitter(IDataEmitterContainer sink) {
        this.emitterContainer = sink;
    }

    @ManagedAttribute
    public int getEmitterThreadCount() {
        return emitterThreadCount;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setDataPipe(DataPipe dataPipe) {
        this.dataPipe = dataPipe;
    }

    public void setEmitterThreadCount(int sinkThreadCount) {
        this.emitterThreadCount = sinkThreadCount;
    }

    @ManagedAttribute
    @Override
    public boolean isRunning() {
        return running && isStarted.get();
    }

    @ManagedAttribute
    public String getId() {
        return id;
    }

    public void setStatsCollector(StatsCollector statsCollector) {
        this.statsCollector = statsCollector;
    }

    @ManagedAttribute
    public int getQueueSize() {
        return dataPipe.size();
    }

    @ManagedAttribute
    public int getQueueCapacity() {
        return dataPipe.getCapacity();
    }

    public void setStatsStore(IStatisticsStore statsStore) {
        this.statsStore = statsStore;
    }

    public void setStatsCalculator(StatsCalculator statsCalculator) {
        this.statsCalculator = statsCalculator;
    }

    @ManagedOperation
    @Override
    public void suspend() {
        if (isSuspended.compareAndSet(false, true)) {
            running = false;

            injector.suspend();
            emitterContainer.suspend();

            scheduler.shutdown();

            logger.info("Funnel " + id + " suspended");
        } else
            logger.warn("Funnel " + id + " was already suspended");
    }

    @ManagedOperation
    public void drainThenSuspend() {
        if (isSuspended.compareAndSet(false, true)) {
            running = false;
            injector.suspend();
            CommonUtils.sleepQuietly(10);
            emitterContainer.drainThenSuspend();

            scheduler.shutdown();

            logger.info("Funnel " + id + " suspended");
        } else
            logger.warn("Funnel " + id + " was already suspended");
    }

    @ManagedOperation
    @Override
    public void resume() {
        if (isSuspended.compareAndSet(true, false)) {
            injector.resume();
            emitterContainer.resume();

            scheduler = CommonUtils.createScheduledExecutorService(1,
                    CommonUtils.resolveThreadName("DataFunnel_" + id));
            schedFuture = scheduler.scheduleAtFixedRate(this, this.initDelaySecs, this.intervalSecs,
                    TimeUnit.SECONDS);

            running = true;

            logger.info("Funnel " + id + " operation resumed");
        } else
            logger.warn("Nothing to do - Funnel " + id + " was not suspended");

    }

    public void setDataPipeClassname(String dataPipeClassname) {
        this.dataPipeClassname = dataPipeClassname;
    }

    @Override
    public void setDataPipeCapacity(int dataPipeCapacity) {
        this.dataPipeCapacity = dataPipeCapacity;
    }

    @Override
    public void setAutoStart(boolean autoStart) {
        this.autoStart = autoStart;
    }

    @ManagedAttribute
    public boolean isAutoStart() {
        return autoStart;
    }

    @ManagedAttribute
    public boolean getIsSuspended() {
        return isSuspended.get();
    }

    @ManagedAttribute
    public int getTargetLimitedRate() {
        return targetLimitedRate;
    }

    @Override
    public void setRateLimit(int targetLimitedRate) {
        this.targetLimitedRate = targetLimitedRate;
    }

    @ManagedOperation
    public void changeRateLimit(int targetLimitedRate) {
        this.targetLimitedRate = targetLimitedRate;
        this.emitterContainer.changeRateLimit(targetLimitedRate);
    }

    @Override
    public void run() {
        if (!isSuspended.get()) {
            if ((!injector.isRunning()) && (!injector.isSuspended())) {
                logger.warn(injector.getId() + " not in running state.  Drain and suspend the funnel");
                this.drainThenSuspend();
            } else if (!emitterContainer.isRunning() && !emitterContainer.isSuspended()) {
                logger.warn(emitterContainer.getId() + " not in running state.  Suspend the funnel");
                this.suspend();
            }
        }
    }

    public void setInitDelaySecs(int initDelaySecs) {
        this.initDelaySecs = initDelaySecs;
    }

    public void setIntervalSecs(int intervalSecs) {
        this.intervalSecs = intervalSecs;
    }

    @ManagedAttribute
    public int getInitDelaySecs() {
        return initDelaySecs;
    }

    @ManagedAttribute
    public int getIntervalSecs() {
        return intervalSecs;
    }
}