001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.util.event.servlet;
035:
036: import com.icesoft.faces.webapp.http.servlet.SessionDispatcher;
037: import org.apache.commons.logging.Log;
038: import org.apache.commons.logging.LogFactory;
039:
040: import javax.servlet.ServletContext;
041: import javax.servlet.ServletContextEvent;
042: import javax.servlet.ServletContextListener;
043: import javax.servlet.http.HttpSession;
044: import javax.servlet.http.HttpSessionEvent;
045: import javax.servlet.http.HttpSessionListener;
046: import java.util.HashMap;
047: import java.util.Iterator;
048: import java.util.Map;
049: import java.util.Set;
050: import java.util.WeakHashMap;
051:
052: /*
053: * The ContextEventRepeater was designed to forward servlet events to different
054: * parts of the ICEfaces framework. These events are typically of interest for
055: * gracefully and/or proactively keeping track of valid sessions and allowing
056: * for orderly shut down.
057: *
058: * This was deemed necessary since the Servlet specification does not allow a
059: * programmatic way of adding and removing listeners for these events. So rather
060: * than have the various ICEfaces pieces register listeners individually, we can
061: * register this class and then add and remove listeners as required.
062: *
063: * The implementation is currently simple and broad. The class maintains a
064: * static collection of listeners in a WeakHashMap and forwards all events to
065: * all registered listeners.
066: *
067: * Future improvements might include:
068: * - adapter implementations
069: * - event filtering
070: *
071: * For now, anything that is interested in receiving events from the repeater
072: * should simply implement the ContextEventListener interface and then add
073: * itself to the ContextEventRepeater using the static addListener method.
074: *
075: * The limitation of adding a listener programmatically is that certain creation
076: * events can occur before the class has a chance to add itself as a listener.
077: * To mitigate this, the ContextEventRepeater buffers the creation events
078: * temporarily. When a ContextEventListener is added, the receiveBufferedEvents
079: * method is called and, if it returns true, any buffered creation events are
080: * sent to the listener after it has been added to the listener collection. The
081: * timing of the events is off but the session information can still be useful.
082: * Events are removed from the buffer when the corresponding destroy events are
083: * received. This means that that sessions that have already been created AND
084: * destroyed are NOT in the buffer.
085: */
086:
087: /**
088: *
089: */
090: public class ContextEventRepeater implements HttpSessionListener,
091: ServletContextListener {
092: //todo: fix it... this is just a temporary solution
093: private static SessionDispatcher.Listener SessionDispatcherListener;
094: static {
095: SessionDispatcherListener = new SessionDispatcher.Listener();
096: }
097:
098: private static final String ASYNC_SERVER_KEY = "com.icesoft.faces.async.server";
099: private static final String MESSAGING_CONTEXT_EVENT_PUBLISHER_CLASS_NAME = "com.icesoft.faces.util.event.servlet.MessagingContextEventPublisher";
100:
101: private static Log log = LogFactory
102: .getLog(ContextEventRepeater.class);
103:
104: private static Map bufferedContextEvents = new HashMap();
105: private static ContextEventPublisher contextEventPublisher;
106: private static Map listeners = new WeakHashMap();
107:
108: /**
109: * Adds the specified <code>listener</code> to this
110: * <code>ContextEventRepeater</code>. </p>
111: *
112: * @param listener the listener to be added.
113: */
114: public synchronized static void addListener(
115: ContextEventListener listener) {
116: if (listener == null || listeners.containsKey(listener)) {
117: return;
118: }
119: listeners.put(listener, null);
120: if (listener.receiveBufferedEvents()) {
121: sendBufferedEvents(listener);
122: }
123: }
124:
125: /**
126: * Fires a new <code>ContextDestroyedEvent</code>, based on the received
127: * <code>event</code>, to all registered listeners, and cleans itself
128: * up. </p>
129: *
130: * @param event the servlet context event.
131: */
132: public synchronized void contextDestroyed(ServletContextEvent event) {
133: SessionDispatcherListener.contextDestroyed(event);
134:
135: ContextDestroyedEvent contextDestroyedEvent = new ContextDestroyedEvent(
136: event);
137: Iterator it = listeners.keySet().iterator();
138: while (it.hasNext()) {
139: ((ContextEventListener) it.next())
140: .contextDestroyed(contextDestroyedEvent);
141: }
142: listeners.clear();
143: bufferedContextEvents.clear();
144: if (contextEventPublisher != null) {
145: contextEventPublisher.publish(contextDestroyedEvent);
146: }
147: if (log.isInfoEnabled()) {
148: ServletContext servletContext = contextDestroyedEvent
149: .getServletContext();
150: log.info("Servlet Context Name: "
151: + servletContext.getServletContextName() + ", "
152: + "Server Info: " + servletContext.getServerInfo());
153: }
154: }
155:
156: public synchronized void contextInitialized(
157: ServletContextEvent event) {
158: SessionDispatcherListener.contextInitialized(event);
159:
160: boolean _asyncServer;
161: String _asyncServerValue = event.getServletContext()
162: .getInitParameter(ASYNC_SERVER_KEY);
163: if (_asyncServerValue != null) {
164: _asyncServer = Boolean.valueOf(_asyncServerValue)
165: .booleanValue();
166: } else {
167: _asyncServer = false;
168: if (log.isDebugEnabled()) {
169: log
170: .debug("com.icesoft.faces.async.server not defined.");
171: }
172: }
173: if (_asyncServer) {
174: try {
175: contextEventPublisher = (ContextEventPublisher) Class
176: .forName(
177: MESSAGING_CONTEXT_EVENT_PUBLISHER_CLASS_NAME)
178: .newInstance();
179: contextEventPublisher.setContextEventRepeater(this );
180: contextEventPublisher
181: .publish(new ContextInitializedEvent(event));
182: } catch (ClassNotFoundException exception) {
183: if (log.isDebugEnabled()) {
184: log
185: .debug("MessagingContextEventPublisher is not found!");
186: }
187: } catch (IllegalAccessException exception) {
188: if (log.isFatalEnabled()) {
189: log.fatal("Failed to access constructor of "
190: + "MessagingContextEventPublisher!",
191: exception);
192: }
193: } catch (InstantiationException exception) {
194: if (log.isFatalEnabled()) {
195: log
196: .fatal(
197: "Failed to "
198: + "instantiate MessagingContextEventPublisher!",
199: exception);
200: }
201: }
202: }
203: }
204:
205: public synchronized static void iceFacesIdDisposed(
206: HttpSession source, String iceFacesId) {
207:
208: ICEfacesIDDisposedEvent iceFacesIdDisposedEvent = new ICEfacesIDDisposedEvent(
209: source, iceFacesId);
210: bufferedContextEvents.put(iceFacesIdDisposedEvent, source);
211: Iterator _listeners = listeners.keySet().iterator();
212: while (_listeners.hasNext()) {
213: ((ContextEventListener) _listeners.next())
214: .iceFacesIdDisposed(iceFacesIdDisposedEvent);
215: }
216: if (contextEventPublisher != null) {
217: contextEventPublisher.publish(iceFacesIdDisposedEvent);
218: }
219: if (log.isTraceEnabled()) {
220: log.trace("ICEfaces ID disposed: "
221: + iceFacesIdDisposedEvent.getICEfacesID());
222: }
223: }
224:
225: /**
226: * Fires a new <code>ICEfacesIDRetrievedEvent</code>, with the specified
227: * <code>source</code> and </code>iceFacesId</code>, to all registered
228: * listeners. </p>
229: *
230: * @param source the source of the event.
231: * @param iceFacesId the ICEfaces ID.
232: */
233: public synchronized static void iceFacesIdRetrieved(
234: HttpSession source, String iceFacesId) {
235:
236: ICEfacesIDRetrievedEvent iceFacesIdRetrievedEvent = new ICEfacesIDRetrievedEvent(
237: source, iceFacesId);
238: bufferedContextEvents.put(iceFacesIdRetrievedEvent, source);
239: Iterator _listeners = listeners.keySet().iterator();
240: while (_listeners.hasNext()) {
241: ((ContextEventListener) _listeners.next())
242: .iceFacesIdRetrieved(iceFacesIdRetrievedEvent);
243: }
244: if (contextEventPublisher != null) {
245: contextEventPublisher.publish(iceFacesIdRetrievedEvent);
246: }
247: if (log.isTraceEnabled()) {
248: log.trace("ICEfaces ID retrieved: "
249: + iceFacesIdRetrievedEvent.getICEfacesID());
250: }
251: }
252:
253: /**
254: * Removes the specified <code>listener</code> from this
255: * <code>ContextEventRepeater</code>. </p>
256: *
257: * @param listener the listener to be removed.
258: */
259: public synchronized static void removeListener(
260: ContextEventListener listener) {
261:
262: if (listener == null) {
263: return;
264: }
265: listeners.remove(listener);
266: }
267:
268: public synchronized void sessionCreated(HttpSessionEvent event) {
269: SessionDispatcherListener.sessionCreated(event);
270: }
271:
272: /**
273: * Fires a new <code>SessionDestroyedEvent</code>, based on the received
274: * <code>event</code>, to all registered listeners. </p>
275: *
276: * @param event the HTTP session event.
277: */
278: public synchronized void sessionDestroyed(HttpSessionEvent event) {
279: SessionDispatcherListener.sessionDestroyed(event);
280: //It's possible to have a valid session that does not contain an
281: //icefacesID. We should not bail out completely. Simply log a message
282: //and return quietly, but do not broadcast this to the listeners. We
283: //may have to change this behaviour in the future if this becomes a more
284: //general purpose utility.
285: String icefacesID = (String) ((HttpSession) event.getSource())
286: .getAttribute("icefacesID");
287: if (icefacesID == null || icefacesID.trim().length() < 1) {
288: if (log.isDebugEnabled()) {
289: log.debug("session does not contain and icefacesID");
290: }
291: return;
292: }
293:
294: SessionDestroyedEvent sessionDestroyedEvent = new SessionDestroyedEvent(
295: event, icefacesID);
296:
297: Iterator _listeners = listeners.keySet().iterator();
298: while (_listeners.hasNext()) {
299: ((ContextEventListener) _listeners.next())
300: .sessionDestroyed(sessionDestroyedEvent);
301: }
302:
303: removeBufferedEvents(event.getSession());
304:
305: if (contextEventPublisher != null) {
306: contextEventPublisher.publish(sessionDestroyedEvent);
307: }
308: if (log.isTraceEnabled()) {
309: log.trace("ICEfaces ID: "
310: + sessionDestroyedEvent.getICEfacesID());
311: }
312: }
313:
314: /**
315: * Fires a new <code>ViewNumberRetrievedEvent</code>, with the specified
316: * <code>source</code> and </code>viewNumber</code>, to all registered
317: * listeners. </p>
318: *
319: * @param source the source of the event.
320: * @param viewNumber the view number.
321: */
322: public synchronized static void viewNumberRetrieved(
323: HttpSession source, String icefacesID, int viewNumber) {
324:
325: ViewNumberRetrievedEvent viewNumberRetrievedEvent = new ViewNumberRetrievedEvent(
326: source, icefacesID, viewNumber);
327:
328: bufferedContextEvents.put(viewNumberRetrievedEvent, source);
329:
330: Iterator _listeners = listeners.keySet().iterator();
331: while (_listeners.hasNext()) {
332: ((ContextEventListener) _listeners.next())
333: .viewNumberRetrieved(viewNumberRetrievedEvent);
334: }
335: if (contextEventPublisher != null) {
336: contextEventPublisher.publish(viewNumberRetrievedEvent);
337: }
338: if (log.isTraceEnabled()) {
339: log.trace("View Number: "
340: + viewNumberRetrievedEvent.getViewNumber());
341: }
342: }
343:
344: ContextEvent[] getBufferedContextEvents() {
345: Set _contextEventSet = bufferedContextEvents.keySet();
346: return (ContextEvent[]) _contextEventSet
347: .toArray(new ContextEvent[_contextEventSet.size()]);
348: }
349:
350: private synchronized static void removeBufferedEvents(
351: HttpSession session) {
352: Iterator it = bufferedContextEvents.keySet().iterator();
353: Object event = null;
354: HttpSession bufferedSession = null;
355: while (it.hasNext()) {
356: event = it.next();
357: bufferedSession = (HttpSession) bufferedContextEvents
358: .get(event);
359: if (bufferedSession.equals(session)) {
360: //bufferedContextEvents.remove(event);
361: it.remove();
362: }
363: }
364: }
365:
366: private synchronized static void sendBufferedEvents(
367: ContextEventListener listener) {
368:
369: Iterator it = bufferedContextEvents.keySet().iterator();
370: while (it.hasNext()) {
371: Object event = it.next();
372: if (event instanceof ICEfacesIDRetrievedEvent) {
373: listener
374: .iceFacesIdRetrieved((ICEfacesIDRetrievedEvent) event);
375: } else if (event instanceof ViewNumberRetrievedEvent) {
376: listener
377: .viewNumberRetrieved((ViewNumberRetrievedEvent) event);
378: }
379: }
380: }
381: }
|