001 /*
002 * This file is part of the Echo Point Project. This project is a
003 * collection of Components that have extended the Echo Web Application
004 * Framework Version 3.
005 *
006 * Version: MPL 1.1
007 *
008 * The contents of this file are subject to the Mozilla Public License Version
009 * 1.1 (the "License"); you may not use this file except in compliance with
010 * the License. You may obtain a copy of the License at
011 * http://www.mozilla.org/MPL/
012 *
013 * Software distributed under the License is distributed on an "AS IS" basis,
014 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
015 * for the specific language governing rights and limitations under the
016 * License.
017 */
018 package echopoint.externalevent;
019
020 import nextapp.echo.webcontainer.*;
021 import nextapp.echo.app.ApplicationInstance;
022 import nextapp.echo.app.TaskQueueHandle;
023
024 import java.io.IOException;
025 import java.util.Enumeration;
026 import java.util.HashMap;
027 import java.util.Iterator;
028 import java.util.Map;
029 import java.util.Set;
030 import java.util.WeakHashMap;
031
032 import javax.servlet.RequestDispatcher;
033 import javax.servlet.http.HttpServletRequest;
034
035 /**
036 * This service is used to listen for external events that come in when
037 * the user follows a URI that has ?sid=ExternalEvent on it.
038 * @author Brad Baker <p>Modified by Mikael Soderman 2009-04-28</p>
039 * @version $Id$
040 */
041 public class ExternalEventMonitorService
042 implements Service {
043
044 /**
045 * The singleton ExternalEventService monitoring service
046 */
047 public static final ExternalEventMonitorService INSTANCE;
048 static {
049 INSTANCE = new ExternalEventMonitorService();
050 ServiceRegistry serviceRegistry = WebContainerServlet.getServiceRegistry();
051 serviceRegistry.add(ExternalEventMonitorService.INSTANCE);
052 }
053 /**
054 * @see nextapp.echo.webcontainer.Service#getId()
055 */
056 public String getId() {
057 return "ExternalEvent";
058 }
059
060 /**
061 * @see nextapp.echo.webcontainer.Service#getVersion()
062 */
063 public int getVersion() {
064 return DO_NOT_CACHE;
065 }
066 /** a weak map of external event monitors that want to know about events */
067 private WeakHashMap weakInterestedParties = new WeakHashMap();
068
069 /** a weak map of applcation instances to task queues */
070 private WeakHashMap weakInstanceQueues = new WeakHashMap();
071
072
073 /**
074 * Registers the <code>ExternalEventMonitor</code> with the service
075 * that is used to invoke external events.
076 *
077 * @param monitor an <code>ExternalEventMonitor</code> to be notified
078 * of external events.
079 */
080 public synchronized void register(ExternalEventMonitor monitor) {
081 weakInterestedParties.put(monitor,null);
082 }
083
084 /**
085 * Deregisters the <code>ExternalEventMonitor</code> with the service
086 * that is used to invoke external events.
087 *
088 * @param monitor an <code>ExternalEventMonitor</code> to be removed from
089 * being notified of external events.
090 */
091 public synchronized void deregister(ExternalEventMonitor monitor) {
092 weakInterestedParties.remove(monitor);
093 }
094
095
096 /**
097 * @see nextapp.echo.webcontainer.Service#service(nextapp.echo.webcontainer.Connection)
098 */
099 public void service(Connection conn) throws IOException {
100 HttpServletRequest request = conn.getRequest();
101 UserInstance ci = (UserInstance) conn.getUserInstance();
102 ApplicationInstance appInstance = ci.getApplicationInstance();
103 if (appInstance != null) {
104 synchronized(this) {
105 Map parameterMap = new HashMap();
106 for (Enumeration e = request.getParameterNames(); e.hasMoreElements();) {
107 String paramName = (String) e.nextElement();
108 String[] paramValues = conn.getRequest().getParameterValues(paramName);
109 parameterMap.put(paramName,paramValues);
110 }
111 final ExternalEvent externalEvent = new ExternalEvent(this,parameterMap);
112
113 //
114 // create a TaskQueue but only once per app instance. As it is weak
115 // it will die with the AppInstance and hence so will the TaskQueue
116 // as its referred to by its AppInstance.
117 TaskQueueHandle taskQueueHandle = (TaskQueueHandle) weakInstanceQueues.get(appInstance);
118 if (taskQueueHandle == null) {
119 taskQueueHandle = appInstance.createTaskQueue();
120 weakInstanceQueues.put(appInstance,taskQueueHandle);
121 }
122 //
123 // run through all registed event monitors but only tell the ones
124 // that belong to the current app instance.
125 Set set = weakInterestedParties.keySet();
126 for (Iterator iter = set.iterator(); iter.hasNext();) {
127 final ExternalEventMonitor monitor = (ExternalEventMonitor) iter.next();
128
129 if (appInstance.equals(monitor.getApplicationInstance())) {
130 Runnable task = new Runnable() {
131 public void run() {
132 monitor.fireExternalEvent(externalEvent);
133 }
134 };
135 // tell the peer and hence the listeners about the event but in
136 // a runnable task so that it executes in the main UI thread.
137 appInstance.enqueueTask(taskQueueHandle,task);
138 }
139 }
140 }
141 }
142
143 // and then redirect them back to the Echo web app
144 redirectToEchoApp(conn);
145 }
146
147 /**
148 * Redirects back to the actual Echo web application so that
149 * the user sees something. It sends back all the
150 * parameters except the sid=ExternalEvent
151 *
152 * @param conn - the connection in play
153 * @throws IOException
154 */
155 private void redirectToEchoApp(Connection conn) throws IOException {
156 HttpServletRequest request = conn.getRequest();
157 String uri = request.getRequestURI();
158 StringBuffer parameters = new StringBuffer();
159 for (Enumeration e = request.getParameterNames(); e.hasMoreElements();) {
160 String paramName = (String) e.nextElement();
161 String[] values = conn.getRequest().getParameterValues(paramName);
162 //
163 // we dont send the sid=ExternalEvent again because we
164 // will then get invoked again. But we do send everything else!
165 if (! paramName.equals("sid")) {
166 for (int i = 0; i < values.length; i++) {
167 if (parameters.length() == 0)
168 parameters.append('?');
169 else
170 parameters.append('&');
171 parameters.append(paramName);
172 parameters.append('=');
173 parameters.append(values[i]);
174 }
175 }
176 }
177 uri = uri + parameters.toString();
178
179 //
180 // Not sure which is a better way to redirect
181 // from this "temporary" page. sendRedirect() works
182 // however I have seen Internet comments that
183 // question this.
184 if (true) {
185 conn.getResponse().sendRedirect(uri);
186 } else {
187 RequestDispatcher dispatcher = conn.getServlet().getServletContext().getRequestDispatcher(uri);
188 try {
189 if (dispatcher == null )
190 throw new IOException("No Request Dispatcher for " + uri);
191 dispatcher.forward(conn.getRequest(),conn.getResponse());
192 } catch (javax.servlet.ServletException se) {
193 throw new IOException("Dispatch ServletException : " + se.toString());
194 }
195 }
196 }
197 }
198