001/**
002 *
003 * Copyright 2003-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.workgroup.util;
018
019import java.lang.reflect.Method;
020import java.util.ArrayList;
021import java.util.ListIterator;
022import java.util.logging.Level;
023import java.util.logging.Logger;
024
025/**
026 * This class is a very flexible event dispatcher which implements Runnable so that it can
027 * dispatch easily from a newly created thread. The usage of this in code is more or less:
028 * create a new instance of this class, use addListenerTriplet to add as many listeners
029 * as desired to be messaged, create a new Thread using the instance of this class created
030 * as the argument to the constructor, start the new Thread instance.<p>
031 *
032 * Also, this is intended to be used to message methods that either return void, or have
033 * a return which the developer using this class is uninterested in receiving.
034 *
035 * @author loki der quaeler
036 */
037public class ListenerEventDispatcher implements Runnable {
038    private static final Logger LOGGER = Logger.getLogger(ListenerEventDispatcher.class.getName());
039
040    protected transient ArrayList<TripletContainer> triplets;
041
042    protected transient boolean hasFinishedDispatching;
043    protected transient boolean isRunning;
044
045    public ListenerEventDispatcher () {
046        super();
047
048        this.triplets = new ArrayList<TripletContainer>();
049
050        this.hasFinishedDispatching = false;
051        this.isRunning = false;
052    }
053
054    /**
055     * Add a listener triplet - the instance of the listener to be messaged, the Method on which
056     *  the listener should be messaged, and the Object array of arguments to be supplied to the
057     *  Method. No attempts are made to determine whether this triplet was already added.<br>
058     *
059     * Messages are dispatched in the order in which they're added via this method; so if triplet
060     *  X is added after triplet Z, then triplet Z will undergo messaging prior to triplet X.<br>
061     *
062     * This method should not be called once the owning Thread instance has been started; if it
063     *  is called, the triplet will not be added to the messaging queue.<br>
064     *
065     * @param listenerInstance the instance of the listener to receive the associated notification
066     * @param listenerMethod the Method instance representing the method through which notification
067     *                          will occur
068     * @param methodArguments the arguments supplied to the notification method
069     */
070    public void addListenerTriplet(Object listenerInstance, Method listenerMethod,
071            Object[] methodArguments)
072    {
073        if (!this.isRunning) {
074            this.triplets.add(new TripletContainer(listenerInstance, listenerMethod,
075                    methodArguments));
076        }
077    }
078
079    /**
080     * @return whether this instance has finished dispatching its messages
081     */
082    public boolean hasFinished() {
083        return this.hasFinishedDispatching;
084    }
085
086    public void run() {
087        ListIterator<TripletContainer> li = null;
088
089        this.isRunning = true;
090
091        li = this.triplets.listIterator();
092        while (li.hasNext()) {
093            TripletContainer tc = li.next();
094
095            try {
096                tc.getListenerMethod().invoke(tc.getListenerInstance(), tc.getMethodArguments());
097            } catch (Exception e) {
098                LOGGER.log(Level.SEVERE, "Exception dispatching an event", e);
099            }
100        }
101
102        this.hasFinishedDispatching = true;
103    }
104
105
106    protected class TripletContainer {
107
108        protected Object listenerInstance;
109        protected Method listenerMethod;
110        protected Object[] methodArguments;
111
112        protected TripletContainer (Object inst, Method meth, Object[] args) {
113            super();
114
115            this.listenerInstance = inst;
116            this.listenerMethod = meth;
117            this.methodArguments = args;
118        }
119
120        protected Object getListenerInstance() {
121            return this.listenerInstance;
122        }
123
124        protected Method getListenerMethod() {
125            return this.listenerMethod;
126        }
127
128        protected Object[] getMethodArguments() {
129            return this.methodArguments;
130        }
131    }
132}