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