001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings.session;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.wings.LowLevelEventListener;
018: import org.wings.SComponent;
019: import org.wings.SFrame;
020:
021: import java.util.*;
022:
023: /**
024: * Registers session component instants which want to receive low level events.
025: * The dispatcher holds a list of all known low level event listeners and is responsible
026: * to dispatch the according part of an original HTTP request to the
027: * {@link LowLevelEventListener#processLowLevelEvent(String, String[])} method of the registered
028: * {@link LowLevelEventListener}s.
029: *
030: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
031: */
032: public final class LowLevelEventDispatcher implements
033: java.io.Serializable {
034: private final transient static Log log = LogFactory
035: .getLog(LowLevelEventDispatcher.class);
036:
037: /**
038: * The name prefix is stored in the
039: * HashMap as key. The value is a Set (ArrayList) of {@link LowLevelEventListener}s
040: */
041: private final HashMap listeners = new HashMap();
042:
043: private String eventEpoch;
044:
045: protected boolean namedEvents = true;
046:
047: public LowLevelEventDispatcher() {
048: }
049:
050: public final void addLowLevelEventListener(
051: LowLevelEventListener gl, String eventId) {
052: List l = (List) listeners.get(eventId);
053: if (l == null) {
054: l = new ArrayList(2);
055: l.add(gl);
056: listeners.put(eventId, l);
057: } else if (!l.contains(gl)) {
058: l.add(gl);
059: }
060: }
061:
062: public final void removeLowLevelEventListener(
063: LowLevelEventListener gl, String eventId) {
064: List l = (List) listeners.get(eventId);
065: if (l != null) {
066: l.remove(gl);
067: if (l.isEmpty()) {
068: listeners.remove(eventId);
069: }
070: }
071: }
072:
073: /**
074: * Returns list of registered low level event listener for the given event id.
075: *
076: * @param eventId The id (HTTP request parameter name) under which the listeners are registered.
077: * @return A list of registered low level event listener for the given event id.
078: */
079: public final List getLowLevelEventListener(String eventId) {
080: final List list = (List) listeners.get(eventId);
081: return list != null ? Collections.unmodifiableList(list)
082: : Collections.EMPTY_LIST;
083: }
084:
085: /**
086: * Register low level event listeners additionally by their component name as event id.
087: * Used for purposes where you use fixed ids vs. dnymaically applied ids.
088: *
089: * @param registerListenerAlsoUnderName if <code>true</code> then components will also receieve
090: * HTTP values under their {@link org.wings.SComponent#getName()}
091: * in addition to {@link org.wings.LowLevelEventListener#getLowLevelEventId()}
092: */
093: public final void setNamedEvents(
094: boolean registerListenerAlsoUnderName) {
095: namedEvents = registerListenerAlsoUnderName;
096: }
097:
098: /**
099: * Registers a low level event listeners (for HTTP request processing).
100: * <p/>
101: * The NamePrefix of the listeners id is used as HTTP requestr parameter name. .
102: *
103: * @param gl listeners
104: */
105: public void register(LowLevelEventListener gl) {
106: if (gl != null) {
107: final String id = gl.getLowLevelEventId();
108:
109: log.debug("register id '" + id + "' (type: "
110: + gl.getClass() + ")");
111: addLowLevelEventListener(gl, id);
112: }
113: }
114:
115: public void unregister(LowLevelEventListener gl) {
116: if (gl != null) {
117: final String id = gl.getLowLevelEventId();
118:
119: log.debug("unregister id '" + id + "' (type: "
120: + gl.getClass() + ")");
121: removeLowLevelEventListener(gl, id);
122: }
123: }
124:
125: /**
126: * dispatch the events, encoded as [name/(multiple)values]
127: * in the HTTP request. the part in front of the UID_DIVIDER ('-') is removed, first.
128: * if the remainder contains an underscore ('_'), only the portion afore will be used
129: * to identify the target component.
130: *
131: * @param name
132: * @param values
133: * @return if the event has been dispatched
134: */
135: public boolean dispatch(String name, String[] values) {
136: boolean result = false;
137: String id = null;
138: String suffix = null;
139: // Does name contain underscores?
140: int dividerIndex = name.indexOf('_');
141: if (dividerIndex > 0) {
142: // Use the part before the first '_' as the
143: // ID to detect the low level event listener
144: id = name.substring(0, dividerIndex);
145: suffix = name.substring(dividerIndex + 1);
146: } else {
147: id = name; // ID equals name in case of no '_'
148: }
149:
150: final List l = (List) listeners.get(id);
151: if (l != null && l.size() > 0) {
152: for (int i = 0; i < l.size(); ++i) {
153: LowLevelEventListener gl = (LowLevelEventListener) l
154: .get(i);
155: if (gl.isEnabled()) {
156: if (!gl.isEpochCheckEnabled()
157: || isEventEpochValid(gl)) {
158: if (!"focus".equals(suffix)
159: || gl instanceof SFrame) {
160: if (log.isDebugEnabled()) {
161: log
162: .debug("processing event '"
163: + name
164: + "' by "
165: + gl.getClass()
166: + "("
167: + gl
168: .getLowLevelEventId()
169: + ")");
170: }
171: gl.processLowLevelEvent(name, values);
172: result = true;
173: }
174: }
175: }
176: }
177: }
178: return result;
179: }
180:
181: protected boolean isEventEpochValid(LowLevelEventListener gl) {
182: if (eventEpoch != null) {
183: SFrame frame = ((SComponent) gl).getParentFrame();
184: if (frame == null) {
185: if (log.isDebugEnabled()) {
186: log.debug("request for dangling component '"
187: + gl.getName() + "'");
188: }
189: unregister(gl);
190: return false;
191: }
192: if (!eventEpoch.equals(frame.getEventEpoch())) {
193: if (log.isDebugEnabled()) {
194: log.debug("### got outdated event '" + gl.getName()
195: + "' from frame '" + frame.getName()
196: + "' --> received epoch: " + eventEpoch
197: + " | expected epoch: "
198: + frame.getEventEpoch());
199: }
200: frame.fireInvalidLowLevelEventListener(gl);
201: frame.reload();
202: return false;
203: }
204: }
205: return true;
206: }
207:
208: void clear() {
209: listeners.clear();
210: }
211:
212: private final List<Runnable> runnables = new LinkedList<Runnable>();
213:
214: public void invokeLater(Runnable runnable) {
215: synchronized (this .runnables) {
216: runnables.add(runnable);
217: }
218: }
219:
220: void invokeRunnables() {
221: synchronized (this .runnables) {
222: for (Iterator iterator = runnables.iterator(); iterator
223: .hasNext();) {
224: Runnable runnable = (Runnable) iterator.next();
225: try {
226: runnable.run();
227: } catch (Throwable e) {
228: log.error(runnable, e);
229: }
230: iterator.remove();
231: }
232: }
233: }
234:
235: protected void setEventEpoch(String epoch) {
236: this.eventEpoch = epoch;
237: }
238: }
|