Package org.jboss.dashboard.commons.events

Source Code of org.jboss.dashboard.commons.events.Publisher

/**
* Copyright (C) 2012 JBoss Inc
*
* 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 org.jboss.dashboard.commons.events;

import java.util.*;
import java.lang.ref.WeakReference;
import org.jboss.dashboard.database.hibernate.HibernateTxFragment;

/**
* The publisher party int the publish/subscribe GoF design pattern.
* <p>WARNING: In order to solve the -<i>lapsed listener</i>- problem the reference
* to the subscriber stored by the Publisher is weak.
* This it means that if the Subscriber reference is lost then is also automatically removed from the
* subscribers list. To avoid this issue ensure that your Subscriber instance is referenced in your object model
* and exists as long as the Publisher is alive.
*/
public class Publisher {

    /**
     * Logger
     */
    private transient static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(Publisher.class);

    /**
     * The event representing all events.
     * <p>NOTE: <code>EVENT_ALL=0</code> is a reserved identifier.
     */
    public static final int EVENT_ALL = 0;

    /**
     * The registered subscribers grouped by eventId.
     */
    protected Map subscribers;

    public Publisher() {
        subscribers = Collections.synchronizedMap(new HashMap());
    }

    /**
     * Register a subscriber interested in ALL events.
     */
    public synchronized void subscribe(Subscriber subscriber) {
        if (subscriber == null) return;
        subscribe(subscriber, EVENT_ALL);
    }

    /**
     * Register a subscriber interested in a single event.
     * @param eventId The event interested in.
     * <p>NOTE: <code>EVENT_ALL=0</code> is a reserved identifier.
     */
    public synchronized void subscribe(Subscriber subscriber, int eventId) {
        if (subscriber == null) return;

        // Discard duplicates.
        unsubscribe(subscriber, eventId);

        // Avoid subscribers to be "leaked" by the process manager.
        // Solve the -lapsed listener- problem.
        List eventSubscribers = (List) subscribers.get(new Integer(eventId));
        if (eventSubscribers == null) {
            eventSubscribers = Collections.synchronizedList(new ArrayList());
            subscribers.put(new Integer(eventId), eventSubscribers);
        }
        eventSubscribers.add(new WeakReference(subscriber));
    }

    /**
     * Subscribe a collection subscribers for a concrete event.
     */
    public synchronized void subscribe(Collection c, int eventId) {
        Iterator it = c.iterator();
        while (it.hasNext()) {
            this.subscribe((Subscriber) it.next(), eventId);
        }
    }

    /**
     * Removes a registered subscriber.
     */
    public synchronized void unsubscribe(Subscriber subscriber) {
        if (subscriber == null) return;

        Iterator it = subscribers.keySet().iterator();
        while (it.hasNext()) {
            Integer eventId = (Integer) it.next();
            unsubscribe(subscriber, eventId.intValue());
        }
    }

    /**
     * Removes a registered subscriber.
     * @param eventId The event interested in.
     */
    public synchronized void unsubscribe(Subscriber subscriber, int eventId) {
        if (subscriber == null) return;

        List eventSubscribers = (List) subscribers.get(new Integer(eventId));
        if (eventSubscribers == null) return;
        Iterator it = eventSubscribers.iterator();
        while (it.hasNext()) {
            WeakReference wr = (WeakReference) it.next();
            Subscriber regSubscr = (Subscriber) wr.get();
            if (regSubscr == null || regSubscr.equals(subscriber)) it.remove();
        }
    }

    /**
     * Retrieve registered subscribers.
     * @return A map of eventId (Integer) / Subscriber (List).
     * If no subscribers are registered for an event then no entry is returned.
     */
    public synchronized Map getSubscribers() {
        Map results = new HashMap();
        Iterator it = subscribers.keySet().iterator();
        while (it.hasNext()) {
            Integer eventId = (Integer) it.next();
            results.put(eventId, getSubscribers(eventId.intValue()));
        }
        return results;
    }

    /**
     * Retrieve registered Subscribers for a specified event.
     */
    public synchronized List getSubscribers(int eventId) {
        List results = Collections.synchronizedList(new ArrayList());
        List eventSubscribers = (List) subscribers.get(new Integer(eventId));
        if (eventSubscribers == null) return results;

        Iterator it = eventSubscribers.iterator();
        while (it.hasNext()) {
            WeakReference wr = (WeakReference) it.next();
            Subscriber subscriber = (Subscriber) wr.get();
            if (subscriber == null) it.remove();
            else results.add(subscriber);
        }
        return Collections.unmodifiableList(results);
    }

    /**
     * Interface used to fire events.
     * @param eventInfo The object where event occurs.
     */
    public void notifyEvent(final int eventId, final Object eventInfo) {
        // Calculate the target subscribers.
        // Those subscribed to ALL_EVENT must also be taken.
        Set _subscribersToNotify = new HashSet();
        _subscribersToNotify.addAll(getSubscribers(eventId));
        _subscribersToNotify.addAll(getSubscribers(EVENT_ALL));
        Iterator it = _subscribersToNotify.iterator();
        while (it.hasNext()) {
            try {
                final Subscriber subscriber = (Subscriber) it.next();
                switch (subscriber.getTransactionContext()) {

                    // Notify to subscriber right now within the current transaction and thread.
                    case Subscriber.TXCONTEXT_DEFAULT:
                        subscriber.notifyEvent(eventId, eventInfo);
                        break;

                        // Notify to subscriber just before the current transaction completes succesfully.
                        case Subscriber.TXCONTEXT_BEFORE_COMMIT:
                            new HibernateTxFragment(false, true) {
                            protected void beforeCommit() throws Exception {
                                subscriber.notifyEvent(eventId, eventInfo);
                            }}.execute();
                            break;

                    // Notify to subscriber after the current transaction completes succesfully.
                    case Subscriber.TXCONTEXT_AFTER_COMMIT:
                        new HibernateTxFragment(false, true) {
                        protected void afterCommit() throws Exception {
                            subscriber.notifyEvent(eventId, eventInfo);
                        }}.execute();
                        break;

                    // Notify to subscriber within a new transaction launched in a separated thread.
                    case Subscriber.TXCONTEXT_NEW_THREAD:
                        new HibernateTxFragment(false, true) {
                        protected void afterCommit() throws Exception {
                            new Thread(new Runnable() {
                            public void run() {
                                subscriber.notifyEvent(eventId, eventInfo);
                            }}).start();
                        }}.execute();
                        break;

                    // Not supported notification mode.
                    default:
                        throw new IllegalArgumentException("Subscriber notification mode not supported: " + subscriber.getTransactionContext());
                }
            }
            catch (Throwable e) {
                if (e instanceof RuntimeException) throw (RuntimeException) e;
                else throw new RuntimeException("Exception occurred when firing event: " + eventId, e);
            }
        }
    }
}
TOP

Related Classes of org.jboss.dashboard.commons.events.Publisher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.