001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 */ 017 018 package org.apache.commons.lang3.event; 019 020 import java.lang.reflect.InvocationHandler; 021 import java.lang.reflect.Method; 022 import java.lang.reflect.Proxy; 023 import java.util.List; 024 import java.util.concurrent.CopyOnWriteArrayList; 025 026 import org.apache.commons.lang3.Validate; 027 028 /** 029 * An EventListenerSupport object can be used to manage a list of event 030 * listeners of a particular type. The class provides 031 * {@link #addListener(Object)} and {@link #removeListener(Object)} methods 032 * for registering listeners, as well as a {@link #fire()} method for firing 033 * events to the listeners. 034 * 035 * <p/> 036 * To use this class, suppose you want to support ActionEvents. You would do: 037 * <code><pre> 038 * public class MyActionEventSource 039 * { 040 * private EventListenerSupport<ActionListener> actionListeners = 041 * EventListenerSupport.create(ActionListener.class); 042 * 043 * public void someMethodThatFiresAction() 044 * { 045 * ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "somethingCool"); 046 * actionListeners.fire().actionPerformed(e); 047 * } 048 * } 049 * </pre></code> 050 * 051 * @param <L> the type of event listener that is supported by this proxy. 052 * 053 * @since 3.0 054 * @version $Id: EventListenerSupport.java 978864 2010-07-24 12:49:38Z jcarman $ 055 */ 056 public class EventListenerSupport<L> 057 { 058 /** 059 * The list used to hold the registered listeners. This list is 060 * intentionally a thread-safe copy-on-write-array so that traversals over 061 * the list of listeners will be atomic. 062 */ 063 private final List<L> listeners = new CopyOnWriteArrayList<L>(); 064 065 /** 066 * The proxy representing the collection of listeners. Calls to this proxy 067 * object will sent to all registered listeners. 068 */ 069 private final L proxy; 070 071 /** 072 * Creates an EventListenerSupport object which supports the specified 073 * listener type. 074 * 075 * @param listenerInterface the type of listener interface that will receive 076 * events posted using this class. 077 * 078 * @return an EventListenerSupport object which supports the specified 079 * listener type. 080 * 081 * @throws NullPointerException if <code>listenerInterface</code> is 082 * <code>null</code>. 083 * @throws IllegalArgumentException if <code>listenerInterface</code> is 084 * not an interface. 085 */ 086 public static <T> EventListenerSupport<T> create(Class<T> listenerInterface) 087 { 088 return new EventListenerSupport<T>(listenerInterface); 089 } 090 091 /** 092 * Creates an EventListenerSupport object which supports the provided 093 * listener interface. 094 * 095 * @param listenerInterface the type of listener interface that will receive 096 * events posted using this class. 097 * 098 * @throws NullPointerException if <code>listenerInterface</code> is 099 * <code>null</code>. 100 * @throws IllegalArgumentException if <code>listenerInterface</code> is 101 * not an interface. 102 */ 103 public EventListenerSupport(Class<L> listenerInterface) 104 { 105 this(listenerInterface, Thread.currentThread().getContextClassLoader()); 106 } 107 108 /** 109 * Creates an EventListenerSupport object which supports the provided 110 * listener interface using the specified class loader to create the JDK 111 * dynamic proxy. 112 * 113 * @param listenerInterface the listener interface. 114 * @param classLoader the class loader. 115 * 116 * @throws NullPointerException if <code>listenerInterface</code> or 117 * <code>classLoader</code> is <code>null</code>. 118 * @throws IllegalArgumentException if <code>listenerInterface</code> is 119 * not an interface. 120 */ 121 public EventListenerSupport(Class<L> listenerInterface, ClassLoader classLoader) 122 { 123 Validate.notNull(listenerInterface, "Listener interface cannot be null."); 124 Validate.notNull(classLoader, "ClassLoader cannot be null."); 125 Validate.isTrue(listenerInterface.isInterface(), 126 "Class {0} is not an interface", 127 listenerInterface.getName()); 128 proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader, 129 new Class[]{listenerInterface}, 130 new ProxyInvocationHandler())); 131 } 132 133 /** 134 * Returns a proxy object which can be used to call listener methods on all 135 * of the registered event listeners. All calls made to this proxy will be 136 * forwarded to all registered listeners. 137 * 138 * @return a proxy object which can be used to call listener methods on all 139 * of the registered event listeners 140 */ 141 public L fire() 142 { 143 return proxy; 144 } 145 146 //********************************************************************************************************************** 147 // Other Methods 148 //********************************************************************************************************************** 149 150 /** 151 * Registers an event listener. 152 * 153 * @param listener the event listener (may not be <code>null</code>). 154 * 155 * @throws NullPointerException if <code>listener</code> is 156 * <code>null</code>. 157 */ 158 public void addListener(L listener) 159 { 160 Validate.notNull(listener, "Listener object cannot be null."); 161 listeners.add(listener); 162 } 163 164 /** 165 * Returns the number of registered listeners. 166 * 167 * @return the number of registered listeners. 168 */ 169 int getListenerCount() 170 { 171 return listeners.size(); 172 } 173 174 /** 175 * Unregisters an event listener. 176 * 177 * @param listener the event listener (may not be <code>null</code>). 178 * 179 * @throws NullPointerException if <code>listener</code> is 180 * <code>null</code>. 181 */ 182 public void removeListener(L listener) 183 { 184 Validate.notNull(listener, "Listener object cannot be null."); 185 listeners.remove(listener); 186 } 187 188 /** 189 * An invocation handler used to dispatch the event(s) to all the listeners. 190 */ 191 private class ProxyInvocationHandler implements InvocationHandler 192 { 193 /** 194 * Propagates the method call to all registered listeners in place of 195 * the proxy listener object. 196 * 197 * @param proxy the proxy object representing a listener on which the 198 * invocation was called. 199 * @param method the listener method that will be called on all of the 200 * listeners. 201 * @param args event arguments to propogate to the listeners. 202 */ 203 public Object invoke(Object proxy, Method method, Object[] args) 204 throws Throwable 205 { 206 for (L listener : listeners) 207 { 208 method.invoke(listener, args); 209 } 210 return null; 211 } 212 } 213 }