View Javadoc

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