com.almende.eve.algorithms.EventBus.java Source code

Java tutorial

Introduction

Here is the source code for com.almende.eve.algorithms.EventBus.java

Source

/*
 * Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
 * License: The Apache Software License, Version 2.0
 */
package com.almende.eve.algorithms;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.joda.time.DateTime;

import com.almende.eve.protocol.jsonrpc.annotation.Access;
import com.almende.eve.protocol.jsonrpc.annotation.AccessType;
import com.almende.eve.protocol.jsonrpc.annotation.Name;
import com.almende.eve.protocol.jsonrpc.annotation.Namespace;
import com.almende.eve.protocol.jsonrpc.formats.Caller;
import com.almende.eve.protocol.jsonrpc.formats.JSONRequest;
import com.almende.eve.protocol.jsonrpc.formats.Params;
import com.almende.eve.scheduling.Scheduler;
import com.almende.util.jackson.JOM;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * The Class EventBus.
 */
@Namespace("event")
public class EventBus {
    private static final Logger LOG = Logger.getLogger(EventBus.class.getName());
    private Scheduler scheduler = null;
    private TrickleRPC trickle = null;
    private Caller caller = null;
    private Graph neighbors = null;
    private String tag = null;
    private Set<Event> events = new HashSet<Event>(2);

    private long expiryInterval = 500;

    private static final JSONRequest EXPIRYREQUEST = new JSONRequest("event.scheduleExpiry", null);
    private static final JSONRequest TRIGGERREQUEST = new JSONRequest("event.scheduleTrigger", null);

    /**
     * Instantiates a new event bus.
     *
     * @param scheduler
     *            the scheduler
     * @param caller
     *            the caller
     * @param neighbors
     *            the neighbors
     * @param tag
     *            the tag
     */
    public EventBus(Scheduler scheduler, Caller caller, Graph neighbors, String tag) {
        this.scheduler = scheduler;
        this.caller = caller;
        this.neighbors = neighbors;
        this.tag = tag;
        scheduleExpiry();
        scheduleTrigger();
        setupGossip();
    }

    /**
     * Send a message to all EventBus participants, with the default expiry age
     * of 1 minute (60000 millis)
     *
     * @param message
     *            the message
     */
    public void sendEvent(JSONRequest message) {
        sendEvent(message, 60000);
    }

    /**
     * Send event.
     *
     * @param message
     *            the message
     * @param expiryAge
     *            the expiry age
     */
    public void sendEvent(JSONRequest message, long expiryAge) {
        if (expiryAge > 0 && expiryAge < expiryInterval) {
            // speed up expiry after next run.
            expiryInterval = expiryAge;
        }
        final Event event = new Event(DateTime.now().plus(expiryAge).getMillis(), message,
                caller.getSenderUrls().get(0));
        synchronized (events) {
            events.add(event);
        }
        trickle.reset();
    }

    /**
     * Schedule expiry.
     */
    @Access(AccessType.PUBLIC)
    public void scheduleExpiry() {
        doExpiry();
        scheduler.schedule(EXPIRYREQUEST.getId().asText(), EXPIRYREQUEST, DateTime.now().plus(expiryInterval));
    }

    /**
     * Expire old events.
     */
    @Access(AccessType.PUBLIC)
    public void doExpiry() {
        final List<Event> stillToTrigger = new ArrayList<Event>();
        synchronized (events) {
            Iterator<Event> iter = events.iterator();
            while (iter.hasNext()) {
                Event event = iter.next();
                if (event.getExpiryTime() < DateTime.now().getMillis()) {
                    if (!event.isTriggered()) {
                        stillToTrigger.add(event);
                    }
                    iter.remove();
                }
            }
        }
        for (Event event : stillToTrigger) {
            trigger(event);
        }
    }

    /**
     * Schedule trigger.
     */
    @Access(AccessType.PUBLIC)
    public void scheduleTrigger() {
        doTriggers();
        scheduler.schedule(TRIGGERREQUEST.getId().asText(), TRIGGERREQUEST, DateTime.now().plus(5000));
    }

    /**
     * Do triggers, as a precaution if the normal triggering fails.
     */
    @Access(AccessType.PUBLIC)
    public void doTriggers() {
        Event[] eventArray;
        synchronized (this.events) {
            eventArray = this.events.toArray(new Event[0]);
        }
        for (Event event : eventArray) {
            if (!event.isTriggered()) {
                trigger(event);
            }
        }
    }

    private void trigger(final Event event) {
        synchronized (event) {
            if (!event.isTriggered()) {
                event.setTriggered(true);
            } else {
                return;
            }
        }
        scheduler.schedule(null, event.getMessage(), 0);
    }

    private void setupGossip() {
        final ObjectNode config = JOM.createObjectNode();
        config.put("intervalFactor", 16);
        config.put("intervalMin", 10);
        config.put("redundancyFactor", 4);
        config.put("namespace", "event.");

        trickle = new TrickleRPC(config, scheduler, new Runnable() {
            @Override
            public void run() {
            }
        }, new Runnable() {
            @Override
            public void run() {
                final Edge[] neighborArray = neighbors.getByTag(tag);
                final Params params = new Params();
                synchronized (events) {
                    params.add("events", events);
                }
                final JSONRequest request = new JSONRequest("event.receiveEvents", params);
                for (Edge neighbor : neighborArray) {
                    try {
                        caller.call(neighbor.getAddress(), request);
                    } catch (IOException e) {
                        LOG.log(Level.WARNING, "EventBus got IO error", e);
                    }
                }
            }
        });

    }

    /**
     * Receive events.
     *
     * @param events
     *            the events
     */
    @Access(AccessType.PUBLIC)
    public void receiveEvents(final @Name("events") Set<Event> events) {
        boolean trickleReset = false;
        synchronized (this.events) {
            if (!this.events.equals(events)) {
                for (Event event : events) {
                    if (DateTime.now().isBefore(event.getExpiryTime())) {
                        this.events.add(event);
                        trickleReset = true;
                    }
                }
            }
        }
        if (trickleReset) {
            trickle.reset();
        } else {
            trickle.incr();
        }
        Event[] eventArray;
        synchronized (this.events) {
            eventArray = this.events.toArray(new Event[0]);
        }
        for (Event event : eventArray) {
            if (!event.isTriggered()) {
                trigger(event);
            }
        }
    }

    /**
     * Gets the trickle.
     *
     * @return the trickle
     */
    @Namespace("trickle")
    public TrickleRPC getTrickle() {
        return trickle;
    }
}