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<>();
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        if (!this.isRunning) {
073            this.triplets.add(new TripletContainer(listenerInstance, listenerMethod,
074                    methodArguments));
075        }
076    }
077
078    /**
079     * Has finished.
080     * @return whether this instance has finished dispatching its messages
081     */
082    public boolean hasFinished() {
083        return this.hasFinishedDispatching;
084    }
085
086    @Override
087    public void run() {
088        ListIterator<TripletContainer> li;
089
090        this.isRunning = true;
091
092        li = this.triplets.listIterator();
093        while (li.hasNext()) {
094            TripletContainer tc = li.next();
095
096            try {
097                tc.getListenerMethod().invoke(tc.getListenerInstance(), tc.getMethodArguments());
098            } catch (Exception e) {
099                LOGGER.log(Level.SEVERE, "Exception dispatching an event", e);
100            }
101        }
102
103        this.hasFinishedDispatching = true;
104    }
105
106
107    protected static class TripletContainer {
108
109        protected Object listenerInstance;
110        protected Method listenerMethod;
111        protected Object[] methodArguments;
112
113        protected TripletContainer (Object inst, Method meth, Object[] args) {
114            super();
115
116            this.listenerInstance = inst;
117            this.listenerMethod = meth;
118            this.methodArguments = args;
119        }
120
121        protected Object getListenerInstance() {
122            return this.listenerInstance;
123        }
124
125        protected Method getListenerMethod() {
126            return this.listenerMethod;
127        }
128
129        protected Object[] getMethodArguments() {
130            return this.methodArguments;
131        }
132    }
133}