org.gridgain.grid.kernal.managers.eventstorage.GridEventStorageManager.java Source code

Java tutorial

Introduction

Here is the source code for org.gridgain.grid.kernal.managers.eventstorage.GridEventStorageManager.java

Source

// Copyright (C) GridGain Systems Licensed under GPLv3, http://www.gnu.org/licenses/gpl.html

/*  _________        _____ __________________        _____
 *  __  ____/___________(_)______  /__  ____/______ ____(_)_______
 *  _  / __  __  ___/__  / _  __  / _  / __  _  __ `/__  / __  __ \
 *  / /_/ /  _  /    _  /  / /_/ /  / /_/ /  / /_/ / _  /  _  / / /
 *  \____/   /_/     /_/   \_,__/   \____/   \__,_/  /_/   /_/ /_/
 */

package org.gridgain.grid.kernal.managers.eventstorage;

import org.apache.commons.lang.*;
import org.gridgain.grid.*;
import org.gridgain.grid.events.*;
import org.gridgain.grid.kernal.*;
import org.gridgain.grid.kernal.managers.*;
import org.gridgain.grid.kernal.managers.communication.*;
import org.gridgain.grid.kernal.managers.deployment.*;
import org.gridgain.grid.lang.*;
import org.gridgain.grid.lang.utils.*;
import org.gridgain.grid.spi.*;
import org.gridgain.grid.spi.eventstorage.*;
import org.gridgain.grid.typedef.*;
import org.gridgain.grid.typedef.internal.*;
import org.gridgain.grid.util.future.*;
import org.jetbrains.annotations.*;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;

import static org.gridgain.grid.GridEventType.*;
import static org.gridgain.grid.kernal.GridTopic.*;
import static org.gridgain.grid.kernal.managers.communication.GridIoPolicy.*;

/**
 * Grid event storage SPI manager.
 *
 * @author 2012 Copyright (C) GridGain Systems
 * @version 4.0.2c.12042012
 */
public class GridEventStorageManager extends GridManagerAdapter<GridEventStorageSpi> {
    /** Internally-used events. */
    private static final int[] INT_EVTS;

    /** Events that are never passed into SPI, i.e. hidden from system. */
    private static final int[] HIDDEN_EVTS;

    /**
     * Initialize internally used and hidden events.
     * <p>
     * Internal events are always "recordable" for notification
     * purposes (regardless of whether they were enabled or disabled).
     * But won't be sent down to SPI level if user specifically excluded them.
     * <p>
     * Hidden events are NEVER sent to SPI level. They serve purpose of local
     * notification for the local node.
     */
    static {
        INT_EVTS = new int[6];

        INT_EVTS[0] = EVT_NODE_FAILED;
        INT_EVTS[1] = EVT_NODE_LEFT;
        INT_EVTS[2] = EVT_NODE_JOINED;
        INT_EVTS[3] = EVT_NODE_METRICS_UPDATED;
        INT_EVTS[4] = EVT_NODE_SEGMENTED;
        INT_EVTS[5] = EVT_NODE_RECONNECTED;

        Arrays.sort(INT_EVTS);

        HIDDEN_EVTS = new int[1];

        HIDDEN_EVTS[0] = EVT_NODE_METRICS_UPDATED;
    }

    /** */
    private final ConcurrentMap<Integer, Set<GridLocalEventListener>> lsnrs = new ConcurrentHashMap<Integer, Set<GridLocalEventListener>>();

    /** */
    private RequestListener msgLsnr;

    /** Busy lock to control activity of threads. */
    private ReadWriteLock busyLock = new ReentrantReadWriteLock();

    /** Events of these types should be recorded. */
    private int[] inclEvtTypes;

    /** Events of these types should not be recorded. */
    private int[] exclEvtTypes;

    /** Is local node daemon? */
    private boolean isDaemon;

    /**
     * Constructs manager.
     *
     * @param ctx Kernal context.
     */
    public GridEventStorageManager(GridKernalContext ctx) {
        super(GridEventStorageSpi.class, ctx, ctx.config().getEventStorageSpi());
    }

    /** {@inheritDoc} */
    @Override
    public void printMemoryStats() {
        X.println(">>>");
        X.println(">>> Event storage manager memory stats [grid=" + ctx.gridName() + ']');
        X.println(">>>  lsnrsSize: " + lsnrs.size());
    }

    /**
     * Enters busy state in which manager cannot be stopped.
     *
     * @return {@code true} if entered to busy state.
     */
    private boolean enterBusy() {
        return busyLock.readLock().tryLock();
    }

    /**
     * Leaves busy state.
     */
    private void leaveBusy() {
        busyLock.readLock().unlock();
    }

    /** {@inheritDoc} */
    @SuppressWarnings({ "LockAcquiredButNotSafelyReleased" })
    @Override
    public void onKernalStop0(boolean cancel, boolean wait) {
        // Acquire write lock so that any new thread could not be started.
        busyLock.writeLock().lock();

        GridIoManager io = ctx.io();

        if (io != null) {
            io.removeMessageListener(TOPIC_EVENT.name(), msgLsnr);
            io.removeMessageListener(TOPIC_EVENT);
        }

        msgLsnr = null;

        lsnrs.clear();
    }

    /** {@inheritDoc} */
    @Override
    public void stop(boolean cancel, boolean wait) throws GridException {
        stopSpi();

        if (log.isDebugEnabled())
            log.debug(stopInfo());
    }

    /** {@inheritDoc} */
    @Override
    public void start() throws GridException {
        isDaemon = ctx.isDaemon();

        inclEvtTypes = ctx.config().getIncludeEventTypes();
        exclEvtTypes = ctx.config().getExcludeEventTypes();

        if (!ArrayUtils.isEmpty(inclEvtTypes) && !ArrayUtils.isEmpty(exclEvtTypes))
            throw new GridException("Both 'include' event types and 'exclude' event types cannot be provided "
                    + "in configuration.");

        Map<GridLocalEventListener, int[]> evtLsnrs = ctx.config().getLocalEventListeners();

        if (evtLsnrs != null) {
            for (GridLocalEventListener lsnr : evtLsnrs.keySet())
                addLocalEventListener(lsnr, evtLsnrs.get(lsnr));
        }

        startSpi();

        msgLsnr = new RequestListener();

        ctx.io().addMessageListener(TOPIC_EVENT, msgLsnr);

        if (log.isDebugEnabled())
            log.debug(startInfo());
    }

    /**
     * Records event if it's recordable.
     *
     * @param evt Event to record.
     */
    public void record(GridEvent evt) {
        assert evt != null;

        if (!enterBusy())
            return;

        try {
            int type = evt.type();

            // Override user recordable settings for daemon node.
            if ((isDaemon || isUserRecordable(type)) && !isHiddenEvent(type))
                try {
                    getSpi().record(evt);
                } catch (GridSpiException e) {
                    U.error(log, "Failed to record event: " + evt, e);
                }

            if (isRecordable(type))
                notifyListeners(evt);
        } finally {
            leaveBusy();
        }
    }

    /**
     *
     * @param type Event type.
     * @return Whether or not this is an internal event.
     */
    private boolean isInternalEvent(int type) {
        return ArrayUtils.contains(INT_EVTS, type);
    }

    /**
     * Checks if the event type is user-recordable.
     *
     * @param type Event type to check.
     * @return {@code true} if passed event should be recorded, {@code false} - otherwise.
     */
    private boolean isUserRecordable(int type) {
        return inclEvtTypes != null ? ArrayUtils.contains(inclEvtTypes, type)
                : exclEvtTypes == null || !ArrayUtils.contains(exclEvtTypes, type);
    }

    /**
     * Checks whether or not this event is a hidden system event.
     *
     * @param type Event type to check.
     * @return {@code true} if this is a system hidden event.
     */
    private boolean isHiddenEvent(int type) {
        return ArrayUtils.contains(HIDDEN_EVTS, type);
    }

    /**
     * Checks whether this event type should be recorded. Note that internal event types are
     * always recordable for notification purposes but may not be sent down to SPI level for
     * storage and subsequent querying.
     *
     * @param type Event type to check.
     * @return Whether or not this event type should be recorded.
     */
    public boolean isRecordable(int type) {
        return isInternalEvent(type) || isUserRecordable(type);
    }

    /**
     * Adds local event listener. Note that this method specifically disallow an empty
     * array of event type to prevent accidental subscription for all system event that
     * may lead to a drastic performance decrease.
     *
     * @param lsnr Listener to add.
     * @param types Event types to subscribe listener for.
     */
    public void addLocalEventListener(GridLocalEventListener lsnr, int[] types) {
        assert lsnr != null;
        assert types != null;
        assert types.length > 0;

        if (enterBusy())
            try {
                for (int t : types)
                    getOrCreate(t).add(lsnr);
            } finally {
                leaveBusy();
            }
    }

    public void addLocalEventListener(GridLocalEventListener lsnr, int type, @Nullable int... types) {
        assert lsnr != null;
        assert types != null;

        if (enterBusy())
            try {
                getOrCreate(type).add(lsnr);

                for (int t : types)
                    getOrCreate(t).add(lsnr);
            } finally {
                leaveBusy();
            }
    }

    /**
     * @param type Event type.
     * @return Listeners for given event type.
     */
    private Collection<GridLocalEventListener> getOrCreate(Integer type) {
        Set<GridLocalEventListener> set = lsnrs.get(type);

        if (set == null) {
            set = new GridConcurrentHashSet<GridLocalEventListener>();

            Set<GridLocalEventListener> prev = lsnrs.putIfAbsent(type, set);

            if (prev != null)
                set = prev;
        }

        assert set != null;

        return set;
    }

    /**
     * Removes listener for specified events, if any. If no event types provided - it
     * remove the listener for all its registered events.
     *
     * @param lsnr Listener.
     * @param types Event types.
     * @return Returns {@code true} if removed.
     */
    public boolean removeLocalEventListener(GridLocalEventListener lsnr, @Nullable int... types) {
        assert lsnr != null;

        boolean found = false;

        if (F.isEmpty(types)) {
            for (Set<GridLocalEventListener> set : lsnrs.values())
                if (set.remove(lsnr))
                    found = true;
        } else {
            assert types != null;

            for (int type : types) {
                Set<GridLocalEventListener> set = lsnrs.get(type);

                if (set != null && set.remove(lsnr))
                    found = true;
            }
        }

        return found;
    }

    /**
     *
     * @param p Optional predicate.
     * @param types Event types to wait for.
     * @return Event future.
     */
    public GridFuture<GridEvent> waitForEvent(@Nullable final GridPredicate<? super GridEvent> p, int... types) {
        final GridFutureAdapter<GridEvent> fut = new GridFutureAdapter<GridEvent>(ctx);

        addLocalEventListener(new GridLocalEventListener() {
            @Override
            public void onEvent(GridEvent evt) {
                if (p != null && p.apply(evt) || p == null) {
                    fut.onDone(evt);

                    removeLocalEventListener(this);
                }
            }
        }, F.isEmpty(types) ? GridEventType.EVTS_ALL : types);

        return fut;
    }

    /**
     *
     * @param timeout Timeout.
     * @param c Optional continuation.
     * @param p Optional predicate.
     * @param types Event types to wait for.
     * @return Event.
     * @throws GridException Thrown in case of any errors.
     */
    public GridEvent waitForEvent(long timeout, @Nullable Runnable c,
            @Nullable final GridPredicate<? super GridEvent> p, int... types) throws GridException {
        assert timeout >= 0;

        final GridFutureAdapter<GridEvent> fut = new GridFutureAdapter<GridEvent>(ctx);

        addLocalEventListener(new GridLocalEventListener() {
            @Override
            public void onEvent(GridEvent evt) {
                if (p != null && p.apply(evt) || p == null) {
                    fut.onDone(evt);

                    removeLocalEventListener(this);
                }
            }
        }, types);

        try {
            if (c != null)
                c.run();
        } catch (Exception e) {
            throw new GridException(e);
        }

        return fut.get(timeout);
    }

    /**
     * @param evt Event to notify about.
     */
    private void notifyListeners(GridEvent evt) {
        assert evt != null;

        notifyListeners(lsnrs.get(evt.type()), evt);
    }

    /**
     * @param set Set of listeners.
     * @param evt Grid event.
     */
    private void notifyListeners(@Nullable Collection<GridLocalEventListener> set, GridEvent evt) {
        assert evt != null;

        if (!F.isEmpty(set)) {
            assert set != null;

            for (GridLocalEventListener lsnr : set) {
                try {
                    lsnr.onEvent(evt);
                } catch (Throwable e) {
                    U.error(log, "Unexpected exception in listener notification for event: " + evt, e);
                }
            }
        }
    }

    /**
     * @param p Grid event predicate.
     * @return Collection of grid events.
     */
    public Collection<GridEvent> localEvents(GridPredicate<? super GridEvent>... p) {
        assert p != null;

        return getSpi().localEvents(p);
    }

    /**
     * @param p Grid event predicate.
     * @param nodes Collection of nodes.
     * @param timeout Maximum time to wait for result, if {@code 0}, then wait until result is received.
     * @return Collection of events.
     */
    public GridFuture<List<GridEvent>> remoteEventsAsync(final GridPredicate<? super GridEvent> p,
            final Collection<? extends GridNode> nodes, final long timeout) {
        assert p != null;
        assert nodes != null && !nodes.isEmpty();

        final GridFutureAdapter<List<GridEvent>> fut = new GridFutureAdapter<List<GridEvent>>(ctx);

        ctx.closure().runLocalSafe(new GPR() {
            @Override
            public void run() {
                try {
                    fut.onDone(query(p, nodes, timeout));
                } catch (GridException e) {
                    fut.onDone(e);
                }
            }
        }, true);

        return fut;
    }

    /**
     * @param p Grid event predicate.
     * @param nodes Collection of nodes.
     * @param timeout Maximum time to wait for result, if {@code 0}, then wait until result is received.
     * @return Collection of events.
     * @throws GridException Thrown in case of any errors.
     */
    @SuppressWarnings({ "SynchronizationOnLocalVariableOrMethodParameter", "deprecation" })
    private List<GridEvent> query(GridPredicate<? super GridEvent> p, Collection<? extends GridNode> nodes,
            long timeout) throws GridException {
        assert p != null;
        assert nodes != null;

        if (nodes.isEmpty()) {
            U.warn(log, "Failed to query events for empty nodes collection.");

            return Collections.emptyList();
        }

        GridIoManager ioMgr = ctx.io();

        final List<GridEvent> evts = new ArrayList<GridEvent>();

        final AtomicReference<Throwable> err = new AtomicReference<Throwable>(null);

        final Set<UUID> uids = new HashSet<UUID>();

        final Object qryMux = new Object();

        for (GridNode node : nodes)
            uids.add(node.id());

        GridLocalEventListener evtLsnr = new GridLocalEventListener() {
            @Override
            public void onEvent(GridEvent evt) {
                assert evt instanceof GridDiscoveryEvent;

                synchronized (qryMux) {
                    uids.remove(((GridDiscoveryEvent) evt).eventNodeId());

                    if (uids.isEmpty()) {
                        qryMux.notifyAll();
                    }
                }
            }
        };

        GridMessageListener resLsnr = new GridMessageListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onMessage(UUID nodeId, Object msg) {
                assert nodeId != null;
                assert msg != null;

                if (!(msg instanceof GridEventStorageMessage)) {
                    U.error(log, "Received unknown message: " + msg);

                    return;
                }

                GridEventStorageMessage res = (GridEventStorageMessage) msg;

                synchronized (qryMux) {
                    if (uids.remove(nodeId)) {
                        if (res.events() != null)
                            evts.addAll(res.events());
                    } else
                        U.warn(log,
                                "Received duplicate response (ignoring) [nodeId=" + nodeId + ", msg=" + res + ']');

                    if (res.exception() != null)
                        err.set(res.exception());

                    if (uids.isEmpty() || err.get() != null)
                        qryMux.notifyAll();
                }
            }
        };

        String resTopic = TOPIC_EVENT.name(UUID.randomUUID());

        try {
            addLocalEventListener(evtLsnr, new int[] { EVT_NODE_LEFT, EVT_NODE_FAILED });

            ioMgr.addMessageListener(resTopic, resLsnr);

            GridByteArrayList serFilter = U.marshal(ctx.config().getMarshaller(), p);

            GridDeployment dep = ctx.deploy().deploy(p.getClass(), U.detectClassLoader(p.getClass()));

            if (dep == null)
                throw new GridDeploymentException("Failed to deploy event filter: " + p);

            Serializable msg = new GridEventStorageMessage(resTopic, serFilter, p.getClass().getName(),
                    dep.classLoaderId(), dep.deployMode(), dep.sequenceNumber(), dep.userVersion(),
                    dep.participants());

            ioMgr.send(nodes, TOPIC_EVENT, msg, PUBLIC_POOL);

            if (timeout == 0)
                timeout = Long.MAX_VALUE;

            long now = System.currentTimeMillis();

            // Account for overflow of long value.
            long endTime = now + timeout <= 0 ? Long.MAX_VALUE : now + timeout;

            long delta = timeout;

            Collection<UUID> uidsCp = null;

            synchronized (qryMux) {
                try {
                    while (!uids.isEmpty() && err.get() == null && delta > 0) {
                        qryMux.wait(delta);

                        delta = endTime - System.currentTimeMillis();
                    }
                } catch (InterruptedException e) {
                    throw new GridException("Got interrupted while waiting for event query responses.", e);
                }

                if (err.get() != null)
                    throw new GridException("Failed to query events due to exception on remote node.", err.get());

                if (!uids.isEmpty())
                    uidsCp = new LinkedList<UUID>(uids);
            }

            // Outside of synchronization.
            if (uidsCp != null) {
                for (Iterator<UUID> iter = uidsCp.iterator(); iter.hasNext();)
                    // Ignore nodes that have left the grid.
                    if (ctx.discovery().node(iter.next()) == null)
                        iter.remove();

                if (!uidsCp.isEmpty())
                    throw new GridException(
                            "Failed to receive event query response from following nodes: " + uidsCp);
            }
        } finally {
            ioMgr.removeMessageListener(resTopic, resLsnr);

            removeLocalEventListener(evtLsnr);
        }

        return evts;
    }

    /**
     *
     */
    @SuppressWarnings("deprecation")
    private class RequestListener implements GridMessageListener {
        @SuppressWarnings("deprecation")
        @Override
        public void onMessage(UUID nodeId, Object msg) {
            assert nodeId != null;
            assert msg != null;

            if (!enterBusy())
                return;

            try {
                if (!(msg instanceof GridEventStorageMessage)) {
                    U.warn(log, "Received unknown message: " + msg);

                    return;
                }

                GridEventStorageMessage req = (GridEventStorageMessage) msg;

                GridNode node = ctx.discovery().node(nodeId);

                if (node == null) {
                    U.warn(log, "Failed to resolve sender node that does not exist: " + nodeId);

                    return;
                }

                if (log.isDebugEnabled())
                    log.debug("Received event query request: " + req);

                Throwable ex = null;

                GridPredicate<GridEvent> filter = null;

                Collection<GridEvent> evts;

                try {
                    GridDeployment dep = ctx.deploy().getGlobalDeployment(req.deploymentMode(),
                            req.filterClassName(), req.filterClassName(), req.sequenceNumber(), req.userVersion(),
                            nodeId, req.classLoaderId(), req.loaderParticipants(), null);

                    if (dep == null)
                        throw new GridDeploymentException("Failed to obtain deployment for event filter "
                                + "(is peer class loading turned on?): " + req);

                    filter = U.unmarshal(ctx.config().getMarshaller(), req.filter(), dep.classLoader());

                    // Resource injection.
                    ctx.resource().inject(dep, dep.deployedClass(req.filterClassName()), filter);

                    // Get local events.
                    evts = localEvents(filter);
                } catch (GridException e) {
                    U.error(log, "Failed to query events [nodeId=" + nodeId + ", filter=" + filter + ']', e);

                    evts = Collections.emptyList();

                    ex = e;
                } catch (Throwable e) {
                    U.error(log, "Failed to query events due to user exception [nodeId=" + nodeId + ", filter="
                            + filter + ']', e);

                    evts = Collections.emptyList();

                    ex = e;
                }

                // Response message.
                Serializable res = new GridEventStorageMessage(evts, ex);

                try {
                    if (log.isDebugEnabled())
                        log.debug("Sending event query response to node [nodeId=" + nodeId + "res=" + res + ']');

                    ctx.io().send(node, req.responseTopic(), res, PUBLIC_POOL);
                } catch (GridException e) {
                    U.error(log,
                            "Failed to send event query response to node [node=" + nodeId + ", res=" + res + ']',
                            e);
                }
            } finally {
                leaveBusy();
            }
        }
    }
}