io.s4.core.WindowingPE.java Source code

Java tutorial

Introduction

Here is the source code for io.s4.core.WindowingPE.java

Source

/*
 * Copyright (c) 2011 The S4 Project, http://s4.io.
 * All rights reserved.
 * 
 * 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. See accompanying LICENSE file. 
 */
package io.s4.core;

import io.s4.base.Event;

import java.util.Collection;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections15.buffer.CircularFifoBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Abstract ProcessingElement that can store historical values using a sliding
 * window. Each set of values is called a slot. The concrete class must
 * implement a class (the slot class) where values are stored. Each slot
 * represents a segment of time or a fixed number of events. Slots are
 * consecutive in time or events. The slot object cannot be null.
 * 
 * WHen using time-based slots, use this implementation only if you expect most
 * slots to have values, it is not efficient for sparse event streams.
 */
public abstract class WindowingPE<T> extends ProcessingElement {

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

    final private int numSlots;
    private CircularFifoBuffer<T> circularBuffer;
    final private Timer timer;
    final private long slotDurationInMilliseconds;

    /**
     * Constructor for time-based slots. The abstract method
     * {@link #addPeriodicSlot()} is called periodically.
     * 
     * @param app
     *            the application
     * @param slotDuration
     *            the slot duration in timeUnit
     * @param timeUnit
     *            the unit of time
     * @param numSlots
     *            the number of slots to be stored
     */
    public WindowingPE(App app, long slotDuration, TimeUnit timeUnit, int numSlots) {
        super(app);
        this.numSlots = numSlots;

        if (slotDuration > 0l) {
            slotDurationInMilliseconds = TimeUnit.MILLISECONDS.convert(slotDuration, timeUnit);
            timer = new Timer();
            timer.schedule(new SlotTask(), slotDurationInMilliseconds, slotDurationInMilliseconds);
            logger.trace("TIMER: " + slotDurationInMilliseconds);

        } else {
            slotDurationInMilliseconds = 0;
            timer = null;
        }
    }

    /**
     * 
     * Constructor for the event-based slot. The abstract method
     * {@link #addPeriodicSlot()} must be called by the concrete class.
     * 
     * @param app
     *            the application
     * @param numSlots
     *            the number of slots to be stored
     */
    public WindowingPE(App app, int numSlots) {
        this(app, 0l, null, numSlots);
    }

    abstract protected void processInputEvent(Event event);

    abstract public void processOutputEvent(Event event);

    abstract protected void onCreate();

    abstract protected void onRemove();

    /**
     * This method is called at periodic intervals when a new slot must be put
     * into the buffer. The concrete class must implement the logic required to
     * create a slot. For example, compute statistics from aggregations and get
     * variables ready for the new slot.
     * 
     * If the implementation class doesn't use periodic slots, this method will
     * never be called. Use {@link #addSlot(Object)} instead.
     * 
     * @return the slot object
     */
    abstract protected T addPeriodicSlot();

    /**
     * Add an object to the sliding window. Use it when the window is not
     * periodic. For periodic slots use {@link #addPeriodicSlot()} instead.
     * 
     * @param slot
     */
    protected void addSlot(T slot) {

        if (timer != null) {
            logger.error("Calling method addSlot() in a periodic window is not allowed.");
            return;
        }
        if (circularBuffer == null) {
            circularBuffer = new CircularFifoBuffer<T>(numSlots);
        }
        circularBuffer.add(slot);
    }

    /**
     * 
     * @return the least recently inserted slot
     */
    protected T getOldestSlot() {

        return circularBuffer.get();
    }

    /** Stops the the sliding window. */
    protected void stop() {
        timer.cancel();
    }

    /**
     * 
     * @return the collection of slots
     */
    protected Collection<T> getSlots() {

        return circularBuffer;
    }

    private class SlotTask extends TimerTask {

        @Override
        public void run() {

            logger.trace("START TIMER TASK");

            if (peInstances == null) {
                logger.trace("peInstances is null");
                return;
            }

            /* Iterate over all instances and put a new slot in the buffer. */
            for (Map.Entry<String, ProcessingElement> entry : peInstances.entrySet()) {
                logger.trace("pe id: " + entry.getValue().id);
                @SuppressWarnings("unchecked")
                WindowingPE<T> peInstance = (WindowingPE<T>) entry.getValue();

                if (peInstance.circularBuffer == null) {
                    peInstance.circularBuffer = new CircularFifoBuffer<T>(numSlots);
                }
                synchronized (peInstance) {
                    peInstance.circularBuffer.add(peInstance.addPeriodicSlot());
                }
            }
        }
    }
}