/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk.events; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jajuk.util.Const; import org.jajuk.util.log.Log; /** * Registry of Observers for each of the JajukEvents. Used by ObservationManager * to handle informing observers about events happening in other objects. */ class ObserverRegistry { /** The list of Observers per JajukEvents. */ private final Map<JajukEvents, List<Observer>> hEventComponents = new Hashtable<JajukEvents, List<Observer>>( 10); /** Number of current executions for a given event. */ private static Map<JajukEvent, Integer> canals = new HashMap<JajukEvent, Integer>(10); /** * Calls the update method for each observer <br> * We manage execution canals to limit the number of concurrent executions for * a given event type. This allow to avoid thread number explosion in some * error cases * * @param event The event to execute */ @SuppressWarnings("unchecked") void notifySync(JajukEvent event) { // do the synchronization on canals in two parts to do the "update" without // holding the lock synchronized (canals) { int numberOfExecutions = 0; if (canals.containsKey(event)) { numberOfExecutions = canals.get(event); } if (numberOfExecutions > Const.MAX_EVENT_EXECUTIONS) { Log.warn("Event overflow for : " + event); return; } canals.put(event, numberOfExecutions + 1); } try { JajukEvents subject = event.getSubject(); List<Observer> observers = hEventComponents.get(subject); if (observers == null) { return; } // Iterate on a cloned list to avoid concurrent exceptions observers = (List<Observer>) ((ArrayList<Observer>) observers).clone(); Iterator<Observer> it = observers.iterator(); while (it.hasNext()) { Observer obs = it.next(); if (obs != null) { try { obs.update(event); } catch (Throwable t) { Log.error(t); } } } } finally { synchronized (canals) { int numberOfExecutions = canals.get(event); assert (numberOfExecutions > 0); canals.put(event, numberOfExecutions - 1); // to avoid adding more and more memory via the canals-map, we should remove items when they // reach zero again // the effect on memory is rather small, but it shows up after some time in memory profiles // nevertheless. if (canals.get(event) == 0) { canals.remove(event); } } } } /** * Register an Observer for an event. * * @param subject The event to register for. * @param observer The Observer that should be informed about * the event as soon as it is reported somewhere else. */ synchronized void register(JajukEvents subject, Observer observer) { List<Observer> observers = hEventComponents.get(subject); if (observers == null) { observers = new ArrayList<Observer>(1); hEventComponents.put(subject, observers); } // Add the observer, if it is a high priority observer, put it first in // queue if (!observers.contains(observer)) { if (observer instanceof HighPriorityObserver) { observers.add(0, observer); } else { observers.add(observer); } } } /** * Unregister the Observer from an event. * * @param subject The event to unregister from. * @param observer The Observer that is no longer interested in this event. * * @return true if the event was unregistered, false if it was not * registered (any more) and thus did not need to be removed */ synchronized boolean unregister(JajukEvents subject, Observer observer) { List<Observer> alComponents = hEventComponents.get(subject); if (alComponents != null) { return alComponents.remove(observer); } return false; } /** * Remove any registered item. This is mainly used in UnitTests to * get a clean state again. */ synchronized public void clear() { hEventComponents.clear(); synchronized (canals) { canals.clear(); } } }