001: /*
002: ItsNat Java Web Application Framework
003: Copyright (C) 2007 Innowhere Software Services S.L., Spanish Company
004: Author: Jose Maria Arranz Santamaria
005:
006: This program is free software: you can redistribute it and/or modify
007: it under the terms of the GNU Affero General Public License as published by
008: the Free Software Foundation, either version 3 of the License, or
009: (at your option) any later version. See the GNU Affero General Public
010: License for more details. See the copy of the GNU Affero General Public License
011: included in this program. If not, see <http://www.gnu.org/licenses/>.
012: */
013:
014: package org.itsnat.impl.core.client;
015:
016: import java.util.HashSet;
017: import java.util.Iterator;
018: import java.util.Set;
019: import org.itsnat.core.CometNotifier;
020: import org.itsnat.core.ItsNatException;
021: import org.itsnat.core.event.CustomParamTransport;
022: import org.itsnat.core.event.ParamTransport;
023: import org.itsnat.core.event.ContinueEvent;
024: import org.itsnat.impl.core.*;
025: import org.itsnat.impl.core.comet.NormalCometNotifierImpl;
026: import org.itsnat.impl.core.event.EventInternal;
027: import org.itsnat.impl.core.js.domrender.node.JSNodeRenderImpl;
028: import org.itsnat.impl.core.listener.CometTaskEventListenerWrapper;
029: import org.itsnat.impl.core.listener.CometTaskRegistry;
030: import org.itsnat.impl.core.listener.NormalCometTaskRegistry;
031: import org.w3c.dom.events.Event;
032: import org.w3c.dom.events.EventException;
033: import org.w3c.dom.events.EventListener;
034: import org.w3c.dom.events.EventTarget;
035:
036: /**
037: *
038: * @author jmarranz
039: */
040: public class ClientDocumentOwnerImpl extends ClientDocumentImpl {
041: protected ItsNatDocumentImpl itsNatDoc;
042: protected Set cometNotifiers;
043: protected NormalCometTaskRegistry cometTaskRegistry;
044:
045: /**
046: * Creates a new instance of ClientDocumentOwnerImpl
047: */
048: public ClientDocumentOwnerImpl(ItsNatSessionImpl session,
049: ItsNatDocumentImpl itsNatDoc) {
050: super (session);
051:
052: this .itsNatDoc = itsNatDoc;
053: }
054:
055: public ItsNatDocumentImpl getItsNatDocumentImpl() {
056: return itsNatDoc;
057: }
058:
059: public CometNotifier createCometNotifier() {
060: long ajaxTimeout = getItsNatDocument().getAJAXTimeout();
061: return createCometNotifier(ajaxTimeout);
062: }
063:
064: public CometNotifier createCometNotifier(long ajaxTimeout) {
065: return new NormalCometNotifierImpl(ajaxTimeout, this );
066: }
067:
068: public void setInvalid() {
069: super .setInvalid();
070:
071: if (hasCometNotifiers()) {
072: Set notifiers = getCometNotifierSet();
073: for (Iterator it = notifiers.iterator(); it.hasNext();) {
074: NormalCometNotifierImpl notifier = (NormalCometNotifierImpl) it
075: .next();
076: notifier.stop();
077: }
078: notifiers.clear();
079: }
080: }
081:
082: public boolean hasCometNotifiers() {
083: if (cometNotifiers == null)
084: return false;
085: return !cometNotifiers.isEmpty();
086: }
087:
088: public Set getCometNotifierSet() {
089: if (cometNotifiers == null)
090: this .cometNotifiers = new HashSet(); // para ahorrar memoria si no se usa
091: return cometNotifiers;
092: }
093:
094: public void addCometNotifier(NormalCometNotifierImpl notifier) {
095: getCometNotifierSet().add(notifier);
096: }
097:
098: public void removeCometNotifier(NormalCometNotifierImpl notifier) {
099: getCometNotifierSet().remove(notifier);
100: }
101:
102: public CometTaskRegistry getCometTaskRegistry() {
103: if (cometTaskRegistry == null)
104: this .cometTaskRegistry = new NormalCometTaskRegistry(this ); // para ahorrar memoria si no se usa
105: return cometTaskRegistry;
106: }
107:
108: public void addCometTask(NormalCometNotifierImpl notifier) {
109: getCometTaskRegistry().addCometTask(notifier);
110: }
111:
112: public CometTaskEventListenerWrapper removeCometTask(String id) {
113: return getCometTaskRegistry().removeCometTask(id);
114: }
115:
116: public void addCodeToSend(String code, NodeLocation nodeLoc) {
117: // Las llamadas por ejemplo dispatchEvent no deben
118: // enviarse a los remote ctrl observers, pero en el caso de que
119: // exista un nodo que se cachea indirectamente, hay que cachear explícitamente
120: // en el control remoto
121: addCodeToSend(code);
122:
123: getItsNatDocumentImpl().addCodeToSendToClientDocRemoteCtrl(
124: code, nodeLoc);
125: }
126:
127: public void startEventDispatcherThread(final Runnable task) {
128: // El hilo que llama este método debe ser un hilo asociado al request/response
129: final ItsNatDocumentImpl itsNatDoc = getItsNatDocumentImpl();
130: if (!Thread.currentThread().holdsLock(itsNatDoc)
131: || (itsNatDoc.getCurrentItsNatServletRequest() == null))
132: throw new ItsNatException(
133: "Caller thread must be a normal browser-request thread");
134:
135: final Thread thread = new Thread() {
136: public void run() {
137: // Al ser un hilo diferente el document no está sincronizado (no debe estarlo, aunque dentro del hilo puntualmente necesitará sincronizar para acceder al mismo)
138: task.run(); // Normalmente hará llamadas a EventTarget.dispatchEvent
139:
140: synchronized (itsNatDoc) {
141: Set threadSet = itsNatDoc
142: .getEventDispatcherThreadSet();
143: Thread thread = this ; // para que quede clarito
144: threadSet.remove(thread);
145: }
146:
147: itsNatDoc.unlockThreads(); // la última posible llamada a EventTarget.dispatchEvent deja bloqueado el último hilo request/response
148: }
149: };
150:
151: Set threadSet = itsNatDoc.getEventDispatcherThreadSet();
152: threadSet.add(thread);
153:
154: thread.start(); // No importa que empiece el hilo antes de que se pare el hilo actual pues cualquier llamada a EventTarget.dispatchEvent necesita bloquear el documento, el cual está ya bloqueado por este hilo request/response, y hasta que no termine este request/response y libere el documento deberá esperar
155:
156: long evtDispMaxWait = itsNatDoc.getEventDispatcherMaxWait();
157: itsNatDoc.lockThread(evtDispMaxWait);
158: }
159:
160: public boolean dispatchEvent(EventTarget target, Event evt,
161: int syncMode, long ajaxTimeout) throws EventException {
162: final ItsNatDocumentImpl itsNatDoc = getItsNatDocumentImpl();
163: if (Thread.currentThread().holdsLock(itsNatDoc))
164: throw new ItsNatException(
165: "Document must be unlocked in this call");
166:
167: ((EventInternal) evt).checkInitializedEvent();
168:
169: final long evtDispMaxWait = itsNatDoc
170: .getEventDispatcherMaxWait();
171: final boolean[] monitor = new boolean[1];
172:
173: synchronized (itsNatDoc) {
174: JSNodeRenderImpl.addCodeDispatchEvent(target, evt, "res",
175: this );
176:
177: EventListener listener = new EventListener() {
178: public void handleEvent(Event evt) {
179: ContinueEvent contEvt = (ContinueEvent) evt;
180: // El hilo que ejecuta este método es un hilo request/response
181: monitor[0] = Boolean.getBoolean((String) contEvt
182: .getExtraParam("itsnat_res"));
183: synchronized (monitor) {
184: monitor.notifyAll(); // Desbloquea el hilo dipatcher de eventos
185: }
186: itsNatDoc.lockThread(evtDispMaxWait); // Bloquea el hilo del request/response para una posible siguiente llamada a addCodeDispatchEvent
187: }
188: };
189: CustomParamTransport param = new CustomParamTransport(
190: "itsnat_res", "res");
191: itsNatDoc.addContinueEventListener(null, listener,
192: syncMode, new ParamTransport[] { param }, null,
193: ajaxTimeout);
194:
195: itsNatDoc.notifyAll(); // Desbloquea el hilo del request/response para que se envíe el código al browser
196: }
197:
198: synchronized (monitor) {
199: // Bloqueamos el hilo dispatcher de eventos esperando la respuesta del navegador
200: try {
201: monitor.wait(evtDispMaxWait);
202: } catch (InterruptedException ex) {
203: throw new ItsNatException(ex);
204: }
205: }
206:
207: return monitor[0];
208: }
209: }
|