Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/event/EventManager.java


1   /* ====================================================================
2    * 
3    * The ObjectStyle Group Software License, Version 1.0 
4    *
5    * Copyright (c) 2002-2003 The ObjectStyle Group 
6    * and individual authors of the software.  All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer. 
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:  
22   *       "This product includes software developed by the 
23   *        ObjectStyle Group (http://objectstyle.org/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The names "ObjectStyle Group" and "Cayenne" 
28   *    must not be used to endorse or promote products derived
29   *    from this software without prior written permission. For written 
30   *    permission, please contact andrus@objectstyle.org.
31   *
32   * 5. Products derived from this software may not be called "ObjectStyle"
33   *    nor may "ObjectStyle" appear in their names without prior written
34   *    permission of the ObjectStyle Group.
35   *
36   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   * SUCH DAMAGE.
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of the ObjectStyle Group.  For more
52   * information on the ObjectStyle Group, please see
53   * <http://objectstyle.org/>.
54   *
55   */ 
56  
57  package org.objectstyle.cayenne.event;
58  
59  import java.util.ArrayList;
60  import java.util.EventListener;
61  import java.util.EventObject;
62  import java.util.HashSet;
63  import java.util.Iterator;
64  import java.util.List;
65  import java.util.Map;
66  import java.util.Set;
67  import java.util.WeakHashMap;
68  
69  import org.apache.commons.collections.iterators.SingletonIterator;
70  import org.objectstyle.cayenne.util.Invocation;
71  
72  /**
73   * This class acts as bridge between an Object that wants to inform others about
74   * its current state or a change thereof (Publisher) and a list of objects
75   * interested in the Subject (Listeners).
76   * 
77   * @author Dirk Olmes
78   * @author Holger Hoffstaette
79   */
80  public class EventManager extends Object {
81    private static final EventManager _defaultManager = new EventManager();
82  
83    // keeps weak references to subjects
84    private Map _subjects;
85  
86    /**
87     * This method will return the shared 'default' EventManager.
88     * 
89     * @return EventManager the shared EventManager instance
90     */
91    public static EventManager getDefaultManager() {
92      return _defaultManager;
93    }
94  
95    /**
96     * Default constructor for new EventManager instances, in case you need one.
97     */
98    public EventManager() {
99      super();
100     _subjects = new WeakHashMap();
101   }
102 
103   /**
104    * Register  an <code>EventListener</code> for events sent by any sender.
105    * 
106    * @throws NoSuchMethodException if <code>methodName</code> is not found
107    * @see #addListener(EventListener, String, Class, EventSubject, Object)
108    */
109   synchronized public void addListener(EventListener listener,
110                       String methodName,
111                       Class eventParameterClass,
112                       EventSubject subject)
113     throws NoSuchMethodException {
114     this.addListener(listener, methodName, eventParameterClass, subject, this);
115   }
116 
117   /**
118    * Register  an <code>EventListener</code> for events sent by a specific
119    * sender.
120    * 
121    * @param listener the object to be notified about events
122    * @param methodName the name of the listener method to be invoked
123    * @param eventParameterClass the class of the single event argument passed
124    * to <code>methodName</code>
125    * @param subject the event subject that the listener is interested in
126    * @param sender the object whose events the listener is interested in;
127    * <code>null</code> means 'any sender'.
128    * @throws NoSuchMethodException if <code>methodName</code> is not found
129    */
130   synchronized public void addListener(EventListener listener,
131                       String methodName,
132                       Class eventParameterClass,
133                       EventSubject subject,
134                       Object sender)
135     throws NoSuchMethodException {
136     if (listener == null) {
137       throw new IllegalArgumentException("Listener must not be null.");
138     }
139 
140     if (eventParameterClass == null) {
141       throw new IllegalArgumentException("Event class must not be null.");
142     }
143 
144     if (subject == null) {
145       throw new IllegalArgumentException("Subject must not be null.");
146     }
147 
148     Invocation inv = new Invocation(listener, methodName, eventParameterClass);
149 
150     Map subjectQueues = this.invocationQueuesForSubject(subject);
151     if (subjectQueues == null) {
152       // make sure the subject can be associated with invocation queues
153       subjectQueues = new WeakHashMap();
154       _subjects.put(subject, subjectQueues);
155     }
156 
157     Set queueForSender = this.invocationQueueForSubjectAndSender(subject, sender);
158     if (queueForSender == null) {
159       // create a new listener 'queue'; must keep strong references
160       queueForSender = new HashSet();
161       subjectQueues.put(sender, queueForSender);
162     }
163 
164     queueForSender.add(inv);
165   }
166 
167   /**
168    * Unregister the specified listener from all event subjects handled by this
169    * <code>EventManager</code> instance.
170    * 
171    * @param listener the object to be unregistered
172    * @return <code>true</code> if <code>listener</code> could be removed for
173    * any existing subjects, else returns <code>false</code>.
174    */
175   synchronized public boolean removeListener(EventListener listener) {
176     boolean didRemove = false;
177 
178     if ((_subjects.isEmpty() == false) && (listener != null)) {
179       Iterator subjectIter = _subjects.keySet().iterator();
180       while (subjectIter.hasNext()) {
181         didRemove |= this.removeListener(listener, (EventSubject)subjectIter.next());
182       }
183     }
184 
185     return didRemove;
186   }
187 
188   /**
189    * Unregister the specified listener for the events about the given subject.
190    * 
191    * @param listener the object to be unregistered
192    * @param subject the subject from which the listener is to be unregistered
193    * @return <code>true</code> if <code>listener</code> could be removed for
194    * the given subject, else returns <code>false</code>.
195    */
196   synchronized public boolean removeListener(EventListener listener,
197                         EventSubject subject) {
198     return this.removeListener(listener, subject, null);
199   }
200 
201   /**
202    * Unregister the specified listener for the events about the given subject
203    * and the given sender.
204    * 
205    * @param listener the object to be unregistered
206    * @param subject the subject from which the listener is to be unregistered
207    * @param sender the object whose events the listener was interested in;
208    * <code>null</code> means 'any sender'.
209    * @return <code>true</code> if <code>listener</code> could be removed for
210    * the given subject, else returns <code>false</code>.
211    */
212   synchronized public boolean removeListener(EventListener listener,
213                         EventSubject subject,
214                         Object sender) {
215     boolean didRemove = false;
216 
217     if ((listener != null) && (subject != null)) {
218       Map subjectQueues = this.invocationQueuesForSubject(subject);
219       if (subjectQueues != null) {
220         Iterator queueIter;
221         
222         // remove only listeners for sender?
223         if (sender != null) {
224           Set senderQueue = this.invocationQueueForSubjectAndSender(subject, sender);
225           queueIter = new SingletonIterator(senderQueue);
226         }
227         else {
228           queueIter = subjectQueues.values().iterator();
229         }
230 
231         // iterate over all invocation queues for this subject
232         while (queueIter.hasNext()) {
233           Set invocations = (Set)queueIter.next();
234           if ((invocations != null) && (invocations.isEmpty() == false)) {
235             // remove all invocations with the given target
236             Iterator invIter = invocations.iterator();
237             while (invIter.hasNext()) {
238               Invocation inv = (Invocation)invIter.next();
239               if (inv.getTarget() == listener) {
240                 invIter.remove();
241                 didRemove = true;
242               }
243             }
244           }
245         }
246       }
247     }
248   
249     return didRemove;
250   }
251 
252   /**
253    * Sends an event to all registered objects about a particular subject.
254    * 
255    * @param event the event to be posted to the observers
256    * @param subject the subject about which observers will be notified
257    * @throws IllegalArgumentException if event or subject are null
258    */
259   synchronized public void postEvent(EventObject event, EventSubject subject) {
260     if (event == null) {
261       throw new IllegalArgumentException("event must not be null");
262     }
263 
264     if (subject == null) {
265       throw new IllegalArgumentException("subject must not be null");
266     }
267 
268     // collect listener invocations for subject
269     Set specificInvocations = this.invocationQueueForSubjectAndSender(subject, event.getSource());
270     Set defaultInvocations = this.invocationQueueForSubjectAndSender(subject, this);
271     Set[] invocationQueues = new Set[]{specificInvocations, defaultInvocations};
272     Object[] eventArgument = new Object[]{event};
273 
274     for (int i = 0; i < invocationQueues.length; i++) {
275       Set currentQueue = invocationQueues[i];
276       if ((currentQueue != null) && (currentQueue.isEmpty() == false)) {
277         // used to collect all invalid invocations in order to remove
278         // them at the end of this posting cycle
279         List invalidInvocations = null;
280         Iterator iter = currentQueue.iterator();
281         while (iter.hasNext()) {
282           Invocation inv = (Invocation)iter.next();
283           Class[] invParamTypes = inv.getParameterTypes();
284   
285           // we only process event listeners which take exactly
286           // one argument in their registered methods: the passed
287           // event or a valid subclass thereof
288           if ((invParamTypes != null)
289             && (invParamTypes.length == 1)
290             && (invParamTypes[0].isAssignableFrom(event.getClass()))) {
291             // fire invocation, detect if anything went wrong
292             // (e.g. GC'ed invocation targets)
293             if (inv.fire(eventArgument) == false) {
294               if (invalidInvocations == null) {
295                 invalidInvocations = new ArrayList();
296               }
297     
298               invalidInvocations.add(inv);
299             }
300           }
301         }
302   
303         // clear out all invalid invocations
304         if (invalidInvocations != null) {
305           currentQueue.removeAll(invalidInvocations);
306         }
307       }
308     }
309   }
310 
311   // returns a subject's mapping from senders to registered listener invocations
312   private Map invocationQueuesForSubject(EventSubject subject) {
313     return (Map)_subjects.get(subject);
314   }
315 
316   // returns the registered listener invocations for a particular sender;
317   // the owning event manager instance is used as default sender
318   private Set invocationQueueForSubjectAndSender(EventSubject subject, Object sender) {
319     if (sender == null) {
320       sender = this;
321     }
322 
323     Map subjectEntries = this.invocationQueuesForSubject(subject);
324     Set queue = null;
325 
326     if (subjectEntries != null) {
327       queue = (Set)subjectEntries.get(sender);
328     }
329 
330     return queue;
331   }
332 
333 }
334