Java tutorial
/* * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * "The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations under * the License. * * The Original Code is ICEfaces 1.5 open source software code, released * November 5, 2006. The Initial Developer of the Original Code is ICEsoft * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C) * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved. * * Contributor(s): _____________________. * * Alternatively, the contents of this file may be used under the terms of * the GNU Lesser General Public License Version 2.1 or later (the "LGPL" * License), in which case the provisions of the LGPL License are * applicable instead of those above. If you wish to allow use of your * version of this file only under the terms of the LGPL License and not to * allow others to use your version of this file under the MPL, indicate * your decision by deleting the provisions above and replace them with * the notice and other provisions required by the LGPL License. If you do * not delete the provisions above, a recipient may use your version of * this file under either the MPL or the LGPL License." * */ package com.icesoft.faces.util.event.servlet; import com.icesoft.faces.webapp.http.common.Configuration; import com.icesoft.faces.webapp.http.servlet.CoreMessageService; import com.icesoft.faces.webapp.http.servlet.ServletContextConfiguration; import com.icesoft.faces.webapp.http.servlet.SessionDispatcher; import com.icesoft.net.messaging.Message; import com.icesoft.net.messaging.MessageServiceClient; import com.icesoft.util.Properties; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /* * The ContextEventRepeater was designed to forward servlet events to different * parts of the ICEfaces framework. These events are typically of interest for * gracefully and/or proactively keeping track of valid sessions and allowing * for orderly shut down. * * This was deemed necessary since the Servlet specification does not allow a * programmatic way of adding and removing listeners for these events. So rather * than have the various ICEfaces pieces register listeners individually, we can * register this class and then add and remove listeners as required. * * The implementation is currently simple and broad. The class maintains a * static collection of listeners in a WeakHashMap and forwards all events to * all registered listeners. * * Future improvements might include: * - adapter implementations * - event filtering * * For now, anything that is interested in receiving events from the repeater * should simply implement the ContextEventListener interface and then add * itself to the ContextEventRepeater using the static addListener method. * * The limitation of adding a listener programmatically is that certain creation * events can occur before the class has a chance to add itself as a listener. * To mitigate this, the ContextEventRepeater buffers the creation events * temporarily. When a ContextEventListener is added, the receiveBufferedEvents * method is called and, if it returns true, any buffered creation events are * sent to the listener after it has been added to the listener collection. The * timing of the events is off but the session information can still be useful. * Events are removed from the buffer when the corresponding destroy events are * received. This means that that sessions that have already been created AND * destroyed are NOT in the buffer. */ /** * */ public class ContextEventRepeater implements HttpSessionListener, ServletContextListener { private static final Log LOG = LogFactory.getLog(ContextEventRepeater.class); private static final String BUFFERED_CONTEXT_EVENTS_MESSAGE_TYPE = "BufferedContextEvents"; private static final String CONTEXT_EVENT_MESSAGE_TYPE = "ContextEvent"; //todo: fix it... this is just a temporary solution private static SessionDispatcher.Listener SessionDispatcherListener; static { SessionDispatcherListener = new SessionDispatcher.Listener(); } private static final List BUFFERED_CONTEXT_EVENTS = new ArrayList(); private static final Map LISTENERS = new WeakHashMap(); private static final Object MESSAGE_SERVICE_CLIENT_LOCK = new Object(); private static Configuration servletContextConfiguration; private static String blockingRequestHandlerContext; private static CoreMessageService coreMessageService; private static AnnouncementMessageHandler.Callback callback = new AnnouncementMessageHandler.Callback() { public void publishBufferedContextEvents() { synchronized (BUFFERED_CONTEXT_EVENTS) { ContextEvent[] _contextEvents = (ContextEvent[]) BUFFERED_CONTEXT_EVENTS .toArray(new ContextEvent[BUFFERED_CONTEXT_EVENTS.size()]); if (_contextEvents.length != 0) { StringBuffer _message = new StringBuffer(); for (int i = 0; i < _contextEvents.length; i++) { if (i != 0) { _message.append("\r\n"); } _message.append(createMessage(_contextEvents[i])); } Properties _messageProperties = new Properties(); _messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, blockingRequestHandlerContext); coreMessageService.publish(_message.toString(), _messageProperties, BUFFERED_CONTEXT_EVENTS_MESSAGE_TYPE, MessageServiceClient.PUSH_TOPIC_NAME); } } } }; /** * Adds the specified <code>listener</code> to this * <code>ContextEventRepeater</code>. </p> * * @param contextEventListener the listener to be added. */ public static void addListener(final ContextEventListener contextEventListener) { if (contextEventListener != null) { synchronized (LISTENERS) { if (!LISTENERS.containsKey(contextEventListener)) { LISTENERS.put(contextEventListener, null); if (contextEventListener.receiveBufferedEvents()) { sendBufferedEvents(contextEventListener); } } } } } /** * Fires a new <code>ContextDestroyedEvent</code>, based on the received * <code>event</code>, to all registered listeners, and cleans itself * up. </p> * * @param event the servlet context event. */ public void contextDestroyed(final ServletContextEvent event) { SessionDispatcherListener.contextDestroyed(event); ContextDestroyedEvent contextDestroyedEvent = new ContextDestroyedEvent(event); synchronized (LISTENERS) { Iterator _listeners = LISTENERS.keySet().iterator(); while (_listeners.hasNext()) { ((ContextEventListener) _listeners.next()).contextDestroyed(contextDestroyedEvent); } LISTENERS.clear(); synchronized (BUFFERED_CONTEXT_EVENTS) { BUFFERED_CONTEXT_EVENTS.clear(); } } if (LOG.isInfoEnabled()) { ServletContext servletContext = contextDestroyedEvent.getServletContext(); LOG.info("Servlet Context Name: " + servletContext.getServletContextName() + ", " + "Server Info: " + servletContext.getServerInfo()); } } public void contextInitialized(final ServletContextEvent event) { servletContextConfiguration = new ServletContextConfiguration("com.icesoft.faces", event.getServletContext()); SessionDispatcherListener.contextInitialized(event); } public static void iceFacesIdDisposed(final Object source, final String iceFacesId) { ICEfacesIDDisposedEvent iceFacesIdDisposedEvent = new ICEfacesIDDisposedEvent(source, iceFacesId); synchronized (LISTENERS) { Iterator _listeners = LISTENERS.keySet().iterator(); while (_listeners.hasNext()) { ((ContextEventListener) _listeners.next()).iceFacesIdDisposed(iceFacesIdDisposedEvent); } removeBufferedEvents(iceFacesId); } if (coreMessageService != null) { Properties _messageProperties = new Properties(); _messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, blockingRequestHandlerContext); coreMessageService.publish(createMessage(iceFacesIdDisposedEvent), _messageProperties, CONTEXT_EVENT_MESSAGE_TYPE, MessageServiceClient.PUSH_TOPIC_NAME); } if (LOG.isTraceEnabled()) { LOG.trace("ICEfaces ID disposed: " + iceFacesIdDisposedEvent.getICEfacesID()); } } /** * Fires a new <code>ICEfacesIDRetrievedEvent</code>, with the specified * <code>source</code> and </code>iceFacesId</code>, to all registered * listeners. </p> * * @param source the source of the event. * @param iceFacesId the ICEfaces ID. */ public static void iceFacesIdRetrieved(final Object source, final String iceFacesId) { ICEfacesIDRetrievedEvent iceFacesIdRetrievedEvent = new ICEfacesIDRetrievedEvent(source, iceFacesId); synchronized (LISTENERS) { synchronized (BUFFERED_CONTEXT_EVENTS) { BUFFERED_CONTEXT_EVENTS.add(iceFacesIdRetrievedEvent); Iterator _listeners = LISTENERS.keySet().iterator(); while (_listeners.hasNext()) { ((ContextEventListener) _listeners.next()).iceFacesIdRetrieved(iceFacesIdRetrievedEvent); } } } if (coreMessageService != null) { Properties _messageProperties = new Properties(); _messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, blockingRequestHandlerContext); coreMessageService.publish(createMessage(iceFacesIdRetrievedEvent), _messageProperties, CONTEXT_EVENT_MESSAGE_TYPE, MessageServiceClient.PUSH_TOPIC_NAME); } if (LOG.isTraceEnabled()) { LOG.trace("ICEfaces ID retrieved: " + iceFacesIdRetrievedEvent.getICEfacesID()); } } /** * Removes the specified <code>listener</code> from this * <code>ContextEventRepeater</code>. </p> * * @param contextEventListener the listener to be removed. */ public static void removeListener(final ContextEventListener contextEventListener) { if (contextEventListener != null) { synchronized (LISTENERS) { if (LISTENERS.containsKey(contextEventListener)) { LISTENERS.remove(contextEventListener); } } } } public void sessionCreated(final HttpSessionEvent event) { if (LOG.isDebugEnabled()) { LOG.debug("Session Created event: " + event.getSession().getId()); } } /** * Fires a new <code>SessionDestroyedEvent</code>, based on the received * <code>event</code>, to all registered listeners. </p> * * @param event the HTTP session event. */ public void sessionDestroyed(final HttpSessionEvent event) { if (LOG.isDebugEnabled()) { LOG.debug("Session Destroyed event: " + event.getSession().getId()); } // #3073 directly invoke method on SessionDispatcher for all sessions // (Not just wrapped ones) SessionDispatcherListener.sessionDestroyed(event); SessionDestroyedEvent sessionDestroyedEvent = new SessionDestroyedEvent(event); synchronized (LISTENERS) { Iterator _listeners = LISTENERS.keySet().iterator(); while (_listeners.hasNext()) { ((ContextEventListener) _listeners.next()).sessionDestroyed(sessionDestroyedEvent); } } if (LOG.isTraceEnabled()) { LOG.trace("ICEfaces ID: " + sessionDestroyedEvent.getICEfacesID()); } } public static void setCoreMessageService(final CoreMessageService messageService) { if (messageService != null) { synchronized (MESSAGE_SERVICE_CLIENT_LOCK) { if (coreMessageService == null) { coreMessageService = messageService; coreMessageService.getAnnouncementMessageHandler().addCallback(callback); blockingRequestHandlerContext = servletContextConfiguration .getAttribute("blockingRequestHandlerContext", "push-server"); } } } } public static void viewNumberDisposed(final Object source, final String iceFacesId, final int viewNumber) { ViewNumberDisposedEvent viewNumberDisposedEvent = new ViewNumberDisposedEvent(source, iceFacesId, viewNumber); synchronized (LISTENERS) { Iterator _listeners = LISTENERS.keySet().iterator(); while (_listeners.hasNext()) { ((ContextEventListener) _listeners.next()).viewNumberDisposed(viewNumberDisposedEvent); } removeBufferedEvents(iceFacesId, viewNumber); } if (coreMessageService != null) { Properties _messageProperties = new Properties(); _messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, blockingRequestHandlerContext); coreMessageService.publish(createMessage(viewNumberDisposedEvent), _messageProperties, CONTEXT_EVENT_MESSAGE_TYPE, MessageServiceClient.PUSH_TOPIC_NAME); } if (LOG.isTraceEnabled()) { LOG.trace("View Number disposed: " + viewNumberDisposedEvent.getViewNumber() + " " + "[ICEfaces ID: " + viewNumberDisposedEvent.getICEfacesID() + "]"); } } /** * Fires a new <code>ViewNumberRetrievedEvent</code>, with the specified * <code>source</code> and </code>viewNumber</code>, to all registered * listeners. </p> * * @param source the source of the event. * @param iceFacesId the ICEfaces ID. * @param viewNumber the view number. */ public static void viewNumberRetrieved(final Object source, final String iceFacesId, final int viewNumber) { ViewNumberRetrievedEvent viewNumberRetrievedEvent = new ViewNumberRetrievedEvent(source, iceFacesId, viewNumber); synchronized (LISTENERS) { synchronized (BUFFERED_CONTEXT_EVENTS) { BUFFERED_CONTEXT_EVENTS.add(viewNumberRetrievedEvent); Iterator _listeners = LISTENERS.keySet().iterator(); while (_listeners.hasNext()) { ((ContextEventListener) _listeners.next()).viewNumberRetrieved(viewNumberRetrievedEvent); } } } if (coreMessageService != null) { Properties _messageProperties = new Properties(); _messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, blockingRequestHandlerContext); coreMessageService.publish(createMessage(viewNumberRetrievedEvent), _messageProperties, CONTEXT_EVENT_MESSAGE_TYPE, MessageServiceClient.PUSH_TOPIC_NAME); } if (LOG.isTraceEnabled()) { LOG.trace("View Number retrieved: " + viewNumberRetrievedEvent.getViewNumber() + " " + "[ICEfaces ID: " + viewNumberRetrievedEvent.getICEfacesID() + "]"); } } private static String createMessage(final ContextEvent event) { if (event instanceof ICEfacesIDDisposedEvent) { return "ICEfacesIDDisposed;" + ((ICEfacesIDDisposedEvent) event).getICEfacesID(); } else if (event instanceof ICEfacesIDRetrievedEvent) { return "ICEfacesIDRetrieved;" + ((ICEfacesIDRetrievedEvent) event).getICEfacesID(); } else if (event instanceof ViewNumberDisposedEvent) { return "ViewNumberDisposed;" + ((ViewNumberDisposedEvent) event).getICEfacesID() + ";" + ((ViewNumberDisposedEvent) event).getViewNumber(); } else if (event instanceof ViewNumberRetrievedEvent) { return "ViewNumberRetrieved;" + ((ViewNumberRetrievedEvent) event).getICEfacesID() + ";" + ((ViewNumberRetrievedEvent) event).getViewNumber(); } else { return null; } } private synchronized static void removeBufferedEvents(final String iceFacesId) { synchronized (BUFFERED_CONTEXT_EVENTS) { Iterator _bufferedContextEvents = BUFFERED_CONTEXT_EVENTS.iterator(); while (_bufferedContextEvents.hasNext()) { Object event = _bufferedContextEvents.next(); if ((event instanceof ICEfacesIDRetrievedEvent && ((ICEfacesIDRetrievedEvent) event).getICEfacesID().equals(iceFacesId)) || (event instanceof ViewNumberRetrievedEvent && ((ViewNumberRetrievedEvent) event).getICEfacesID().equals(iceFacesId))) { _bufferedContextEvents.remove(); } } } } private static void removeBufferedEvents(final String iceFacesId, final int viewNumber) { synchronized (BUFFERED_CONTEXT_EVENTS) { Iterator _bufferedContextEvents = BUFFERED_CONTEXT_EVENTS.iterator(); while (_bufferedContextEvents.hasNext()) { Object event = _bufferedContextEvents.next(); if (event instanceof ViewNumberRetrievedEvent && ((ViewNumberRetrievedEvent) event).getICEfacesID().equals(iceFacesId) && ((ViewNumberRetrievedEvent) event).getViewNumber() == viewNumber) { _bufferedContextEvents.remove(); } } } } private synchronized static void sendBufferedEvents(final ContextEventListener contextEventListener) { synchronized (BUFFERED_CONTEXT_EVENTS) { Iterator _bufferedContextEvents = BUFFERED_CONTEXT_EVENTS.iterator(); while (_bufferedContextEvents.hasNext()) { Object event = _bufferedContextEvents.next(); if (event instanceof ICEfacesIDRetrievedEvent) { contextEventListener.iceFacesIdRetrieved((ICEfacesIDRetrievedEvent) event); } else if (event instanceof ViewNumberRetrievedEvent) { contextEventListener.viewNumberRetrieved((ViewNumberRetrievedEvent) event); } } } } }