org.wings.session.LowLevelEventDispatcher Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2000,2005 wingS development team.
*
* This file is part of wingS (http://wingsframework.org).
*
* wingS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Please see COPYING for the complete licence.
*/
package org.wings.session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wings.LowLevelEventListener;
import org.wings.SComponent;
import org.wings.SFrame;
import org.wings.comet.Comet;
import org.wings.comet.Pushable;
import java.util.*;
/**
* Registers session component instants which want to receive low level events.
* The dispatcher holds a list of all known low level event listeners and is responsible
* to dispatch the according part of an original HTTP request to the
* {@link LowLevelEventListener#processLowLevelEvent(String, String[])} method of the registered
* {@link LowLevelEventListener}s.
*
* @author Armin Haaf
*/
public final class LowLevelEventDispatcher
implements java.io.Serializable {
private final transient static Logger log = LoggerFactory.getLogger(LowLevelEventDispatcher.class);
/**
* The name prefix is stored in the
* HashMap as key. The value is a Set (ArrayList) of {@link LowLevelEventListener}s
*/
private final HashMap listeners = new HashMap();
private String eventEpoch;
protected boolean namedEvents = true;
private final Session session;
public LowLevelEventDispatcher(Session session) {
this.session = session;
}
public final void addLowLevelEventListener(LowLevelEventListener gl,
String eventId) {
List l = (List) listeners.get(eventId);
if (l == null) {
l = new ArrayList(2);
l.add(gl);
listeners.put(eventId, l);
} else if (!l.contains(gl)) {
l.add(gl);
}
}
public final void removeLowLevelEventListener(LowLevelEventListener gl,
String eventId) {
List l = (List) listeners.get(eventId);
if (l != null) {
l.remove(gl);
if (l.isEmpty()) {
listeners.remove(eventId);
}
}
}
/**
* Returns list of registered low level event listener for the given event id.
*
* @param eventId The id (HTTP request parameter name) under which the listeners are registered.
* @return A list of registered low level event listener for the given event id.
*/
public final List getLowLevelEventListener(String eventId) {
final List list = (List) listeners.get(eventId);
return list != null ? Collections.unmodifiableList(list) : Collections.emptyList();
}
/**
* Register low level event listeners additionally by their component name as event id.
* Used for purposes where you use fixed ids vs. dnymaically applied ids.
*
* @param registerListenerAlsoUnderName if true
then components will also receieve
* HTTP values under their {@link org.wings.SComponent#getName()}
* in addition to {@link org.wings.LowLevelEventListener#getLowLevelEventId()}
*/
public final void setNamedEvents(boolean registerListenerAlsoUnderName) {
namedEvents = registerListenerAlsoUnderName;
}
/**
* Registers a low level event listeners (for HTTP request processing).
*
* The NamePrefix of the listeners id is used as HTTP requestr parameter name. .
*
* @param gl listeners
*/
public void register(LowLevelEventListener gl) {
if (gl != null) {
final String id = gl.getLowLevelEventId();
log.debug("register id '" + id + "' (type: " + gl.getClass() + ')');
addLowLevelEventListener(gl, id);
}
}
public void unregister(LowLevelEventListener gl) {
if (gl != null) {
final String id = gl.getLowLevelEventId();
log.debug("unregister id '" + id + "' (type: " + gl.getClass() + ')');
removeLowLevelEventListener(gl, id);
}
}
boolean lowLevelEventPhase;
void startLowLevelEventPhase() {
lowLevelEventPhase = true;
}
void endLowLevelEventPhase() {
lowLevelEventPhase = false;
}
public boolean isLowLevelEventPhase() {
return lowLevelEventPhase;
}
/**
* dispatch the events, encoded as [name/(multiple)values]
* in the HTTP request. the part in front of the UID_DIVIDER ('-') is removed, first.
* if the remainder contains an underscore ('_'), only the portion afore will be used
* to identify the target component.
*
* @param name
* @param values
* @return if the event has been dispatched
*/
public boolean dispatch(String name, String... values) {
String id = null;
String suffix = null;
// Does name contain underscores?
int dividerIndex = name.indexOf('_');
if (dividerIndex > 0) {
// Use the part before the first '_' as the
// ID to detect the low level event listener
id = name.substring(0, dividerIndex);
suffix = name.substring(dividerIndex + 1);
} else {
id = name; // ID equals name in case of no '_'
}
final List l = (List) listeners.get(id);
boolean result = false;
if (l != null && l.size() > 0) {
for (Object aL : l) {
LowLevelEventListener gl = (LowLevelEventListener) aL;
if (gl.isEnabled()) {
if (!gl.isEpochCheckEnabled() || isEventEpochValid(gl)) {
if (!"focus".equals(suffix) || gl instanceof SFrame) {
if (log.isDebugEnabled()) {
log.debug("processing event '" + name + "' by " +
gl.getClass() + '(' + gl.getLowLevelEventId() + ')');
}
gl.processLowLevelEvent(name, values);
result = true;
}
}
}
}
}
return result;
}
protected boolean isEventEpochValid(LowLevelEventListener gl) {
if (eventEpoch != null) {
SFrame frame = ((SComponent) gl).getParentFrame();
if (frame == null) {
if (log.isDebugEnabled()) {
log.debug("request for dangling component '" + gl.getName() + '\'');
}
unregister(gl);
return false;
}
if (!eventEpoch.equals(frame.getEventEpoch())) {
if (log.isDebugEnabled()) {
log.debug("### got outdated event '" + gl.getName() + "' from frame '" +
frame.getName() + "' --> received epoch: " + eventEpoch +
" | expected epoch: " + frame.getEventEpoch());
}
frame.fireInvalidLowLevelEventListener(gl);
frame.reload();
return false;
}
}
return true;
}
void clear() {
listeners.clear();
}
private final List runnables = new LinkedList<>();
public void invokeLater(Runnable runnable) {
synchronized (this.runnables) {
runnables.add(runnable);
}
final Comet comet = session.getComet();
if (comet == null) return;
final Pushable pushable = comet.getPushable();
if (SessionManager.getSession() != session) {
synchronized (pushable) {
if (pushable.isValid()) {
pushable.push();
log.info("push called (Pushable: " + pushable + ")\n");
}
}
}
}
public void invokeLater_piggyback(Runnable runnable) {
synchronized (this.runnables) {
runnables.add(runnable);
}
}
void invokeRunnables() {
synchronized (this.runnables) {
for (Iterator iterator = runnables.iterator(); iterator.hasNext();) {
Runnable runnable = null;
try {
runnable = (Runnable)iterator.next();
try {
runnable.run();
}
catch (Throwable e) {
log.error(runnable.toString(), e);
}
iterator.remove();
} catch(ConcurrentModificationException e) {
throw new RuntimeException("Runnable class: " + runnable.getClass().getName(), e);
}
}
}
}
public boolean isRunnablesListEmpty() {
synchronized (this.runnables) {
return runnables.isEmpty();
}
}
protected void setEventEpoch(String epoch) {
this.eventEpoch = epoch;
}
}