/** * Copyright (c) 2009 - 2010 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.utils.event * * This software is licensed under the Artistic License 2.0, * see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php * for details */ package org.appwork.utils.event; import java.util.ArrayList; import java.util.EventListener; /** * The Eventsenderclass is the core of the Eventsystem. it can be used to design * new Eventbroadcaster Systems easily. * * * * @author $Author: unknown$ * */ public abstract class Eventsender<T extends EventListener, TT extends DefaultEvent> { transient protected ArrayList<T> addRequestedListeners = null; /** * List of registered Eventlistener */ // TODO: DO we really need ArrayLists here? transient volatile protected ArrayList<T> listeners = null; private final Object LOCK = new Object(); private volatile long readR = 0; /** * List of Listeners that are requested for removal * */ // We use a removeList to avoid threating problems transient protected ArrayList<T> removeRequestedListeners = null; private volatile long writeR = 0; /** * Creates a new Eventsender Instance */ public Eventsender() { this.listeners = new ArrayList<T>(); this.removeRequestedListeners = new ArrayList<T>(); this.addRequestedListeners = new ArrayList<T>(); } /** * Adds a list of listeners * * @param listener */ public void addAllListener(final ArrayList<T> listener) { for (final T l : listener) { this.addListener(l); } } /** * Add a single Listener * * @param listener */ public void addListener(final T t) { synchronized (this.LOCK) { /* decrease WriteCounter in case we remove the removeRequested */ if (this.removeRequestedListeners.contains(t)) { this.removeRequestedListeners.remove(t); this.writeR--; } /* * increase WriteCounter in case we add addRequestedListeners and t * is not in current listeners list */ if (!this.addRequestedListeners.contains(t) && !this.listeners.contains(t)) { this.addRequestedListeners.add(t); this.writeR++; } } } /** * Fires an Event to all registered Listeners * * @param event * @return */ final public void fireEvent(final int id, final Object... parameters) { ArrayList<T> listeners; synchronized (this.LOCK) { if (this.writeR == this.readR) { /* nothing changed, we can use old pointer to listeners */ if (this.listeners.size() == 0) { return; } listeners = this.listeners; } else { /* create new list with copy of old one */ listeners = new ArrayList<T>(this.listeners); /* remove and add wished items */ listeners.removeAll(this.removeRequestedListeners); this.removeRequestedListeners.clear(); listeners.addAll(this.addRequestedListeners); this.addRequestedListeners.clear(); /* update ReadCounter and pointer to listeners */ this.readR = this.writeR; this.listeners = listeners; if (this.listeners.size() == 0) { return; } } } for (final T t : listeners) { this.fireEvent(t, id, parameters); } synchronized (this.LOCK) { if (this.writeR != this.readR) { /* something changed, lets update the list */ /* create new list with copy of old one */ listeners = new ArrayList<T>(this.listeners); /* remove and add wished items */ listeners.removeAll(this.removeRequestedListeners); this.removeRequestedListeners.clear(); listeners.addAll(this.addRequestedListeners); this.addRequestedListeners.clear(); /* update ReadCounter and pointer to listeners */ this.readR = this.writeR; this.listeners = listeners; } } } /** * * @param t * @param id * @param parameters */ protected void fireEvent(final T listener, final int id, final Object... parameters) { throw new RuntimeException("Not implemented. Overwrite org.appwork.utils.event.Eventsender.fireEvent(T, int, Object...) to use this"); } /** * Abstract fire Event Method. * * @param listener * @param event */ protected abstract void fireEvent(T listener, TT event); final public void fireEvent(final TT event) { if (event == null) { return; } ArrayList<T> listeners; synchronized (this.LOCK) { if (this.writeR == this.readR) { /* nothing changed, we can use old pointer to listeners */ if (this.listeners.size() == 0) { return; } listeners = this.listeners; } else { /* create new list with copy of old one */ listeners = new ArrayList<T>(this.listeners); /* remove and add wished items */ listeners.removeAll(this.removeRequestedListeners); this.removeRequestedListeners.clear(); listeners.addAll(this.addRequestedListeners); this.addRequestedListeners.clear(); /* update ReadCounter and pointer to listeners */ this.readR = this.writeR; this.listeners = listeners; if (this.listeners.size() == 0) { return; } } } for (final T t : listeners) { // final long tt = System.currentTimeMillis(); this.fireEvent(t, event); // System.out.println(t + " " + (System.currentTimeMillis() - tt)); } synchronized (this.LOCK) { if (this.writeR != this.readR) { /* something changed, lets update the list */ /* create new list with copy of old one */ listeners = new ArrayList<T>(this.listeners); /* remove and add wished items */ listeners.removeAll(this.removeRequestedListeners); this.removeRequestedListeners.clear(); listeners.addAll(this.addRequestedListeners); this.addRequestedListeners.clear(); /* update ReadCounter and pointer to listeners */ this.readR = this.writeR; this.listeners = listeners; } } } public ArrayList<T> getListener() { synchronized (this.LOCK) { return new ArrayList<T>(this.listeners); } } public boolean hasListener() { synchronized (this.LOCK) { return listeners.size() > 0; } } public void removeListener(final T t) { synchronized (this.LOCK) { /* decrease WriteCounter in case we remove the addRequest */ if (this.addRequestedListeners.contains(t)) { this.addRequestedListeners.remove(t); this.writeR--; } /* * increase WriteCounter in case we add removeRequest and t is in * current listeners list */ if (!this.removeRequestedListeners.contains(t) && this.listeners.contains(t)) { this.removeRequestedListeners.add(t); this.writeR++; } } } }