View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2011 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  package org.eclipse.jetty.osgi.equinoxtools.console;
14  
15  import java.io.IOException;
16  import java.io.PrintWriter;
17  import java.util.HashMap;
18  import java.util.LinkedList;
19  import java.util.Map;
20  import java.util.Queue;
21  
22  import javax.servlet.ServletException;
23  import javax.servlet.http.HttpServlet;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.eclipse.jetty.continuation.Continuation;
28  import org.eclipse.jetty.continuation.ContinuationSupport;
29  import org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator;
30  import org.eclipse.jetty.osgi.equinoxtools.console.WebConsoleWriterOutputStream.OnFlushListener;
31  import org.eclipse.osgi.framework.console.ConsoleSession;
32  
33  /**
34   * Async servlet with jetty continuations to interact with the equinox console.
35   * Ported from jetty's example 'ChatServlet'
36   */
37  public class EquinoxConsoleContinuationServlet extends HttpServlet implements OnFlushListener
38  {
39  
40      private static final long serialVersionUID = 1L;
41      private Map<String,ConsoleUser> _consoleUsers = new HashMap<String, ConsoleUser>();
42      private WebConsoleSession _consoleSession;
43      private EquinoxChattingSupport _support;
44      
45      /**
46       * @param consoleSession
47       */
48      public EquinoxConsoleContinuationServlet()
49      {
50          
51      }
52      /**
53       * @param consoleSession
54       */
55      public EquinoxConsoleContinuationServlet(WebConsoleSession consoleSession, EquinoxChattingSupport support)
56      {
57          _consoleSession = consoleSession;
58          _support = support;
59      }
60      @Override
61      public void init() throws ServletException
62      {
63          if (_consoleSession == null)
64          {
65              _consoleSession = new WebConsoleSession();
66              WebEquinoxToolsActivator.getContext().registerService(ConsoleSession.class.getName(), _consoleSession, null);
67          }
68          if (_support == null)
69          {
70              _support = new EquinoxChattingSupport(_consoleSession);
71          }
72          _consoleSession.addOnFlushListener(this);
73      }
74      @Override
75      public void destroy()
76      {
77          _consoleSession.removeOnFlushListener(this);
78      }
79  
80      // Serve the HTML with embedded CSS and Javascript.
81      // This should be static content and should use real JS libraries.
82      @Override
83      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
84      {
85          if (request.getParameter("action")!=null)
86              doPost(request,response);
87          else
88              response.sendRedirect(request.getContextPath() + request.getServletPath()
89                  + (request.getPathInfo() != null ? request.getPathInfo() : "") +  "/index.html");
90      }
91  
92      // Handle Ajax calls from browser
93      @Override
94      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
95      {   
96          // Ajax calls are form encoded
97          String action = request.getParameter("action");
98          String message = request.getParameter("message");
99          String username = request.getParameter("user");
100 
101         if (action.equals("join"))
102             join(request,response,username);
103         else if (action.equals("poll"))
104             poll(request,response,username);
105         else if (action.equals("chat"))
106             chat(request,response,username,message);
107     }
108 
109     private synchronized void join(HttpServletRequest request,HttpServletResponse response,String username)
110     throws IOException
111     {
112         ConsoleUser member = new ConsoleUser(username);
113         _consoleUsers.put(username,member); 
114         response.setContentType("text/json;charset=utf-8");
115         PrintWriter out=response.getWriter();
116         out.print("{action:\"join\"}");
117     }
118 
119     private synchronized void poll(HttpServletRequest request,HttpServletResponse response,String username)
120     throws IOException
121     {
122         ConsoleUser member = _consoleUsers.get(username);
123         if (member==null)
124         {
125             response.sendError(503);
126             return;
127         }
128 
129         synchronized(member)
130         {
131             if (member.getMessageQueue().size()>0)
132             {
133                 // Send one chat message
134                 response.setContentType("text/json;charset=utf-8");
135                 StringBuilder buf=new StringBuilder();
136 
137                 buf.append("{\"action\":\"poll\",");
138                 buf.append("\"from\":\"");
139                 buf.append(member.getMessageQueue().poll());
140                 buf.append("\",");
141 
142                 String message = member.getMessageQueue().poll();
143                 int quote=message.indexOf('"');
144                 while (quote>=0)
145                 {
146                     message=message.substring(0,quote)+'\\'+message.substring(quote);
147                     quote=message.indexOf('"',quote+2);
148                 }
149                 buf.append("\"chat\":\"");
150                 buf.append(message);
151                 buf.append("\"}");
152                 byte[] bytes = buf.toString().getBytes("utf-8");
153                 response.setContentLength(bytes.length);
154                 response.getOutputStream().write(bytes);
155             }
156             else 
157             {
158                 Continuation continuation = ContinuationSupport.getContinuation(request);
159                 if (continuation.isInitial()) 
160                 {
161                     // No chat in queue, so suspend and wait for timeout or chat
162                     continuation.setTimeout(20000);
163                     continuation.suspend();
164                     member.setContinuation(continuation);
165                 }
166                 else
167                 {
168                     // Timeout so send empty response
169                     response.setContentType("text/json;charset=utf-8");
170                     PrintWriter out=response.getWriter();
171                     out.print("{action:\"poll\"}");
172                 }
173             }
174         }
175     }
176 
177     private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message)
178     throws IOException
179     {
180         if (!message.endsWith("has joined!"))
181         {
182             _consoleSession.processCommand(message, false);
183         }
184         // Post chat to all members
185         onFlush();
186 
187         response.setContentType("text/json;charset=utf-8");
188         PrintWriter out=response.getWriter();
189         out.print("{action:\"chat\"}");  
190     }
191 
192     /**
193      * Called right after the flush method on the output stream has been executed.
194      */
195     public void onFlush()
196     {
197         Queue<String> pendingConsoleOutputMessages = _support.processConsoleOutput(true, this);
198         for (ConsoleUser m:_consoleUsers.values())
199         {
200             synchronized (m)
201             {
202 //                m.getMessageQueue().add("osgi>"); // from
203 //                m.getMessageQueue().add("something was printed");  // chat
204                 m.getMessageQueue().addAll(pendingConsoleOutputMessages);
205                 
206                 // wakeup member if polling
207                 if (m.getContinuation()!=null)
208                 {
209                     m.getContinuation().resume();
210                     m.setContinuation(null);
211                 }
212             }
213         }
214     }
215     
216     class ConsoleUser
217     {
218         private String _name;
219         private Continuation _continuation;
220         private Queue<String> _queue = new LinkedList<String>();
221         
222         public ConsoleUser(String name)
223         {
224             _name = name;
225         }
226         
227         public String getName() 
228         {
229             return _name;
230         }
231         
232         public void setContinuation(Continuation continuation)
233         {
234             _continuation = continuation;
235         }
236         
237         public Continuation getContinuation()
238         {
239             return _continuation;
240         }
241         public Queue<String> getMessageQueue()
242         {
243             return _queue;
244         }
245         
246     }
247 }