1   //========================================================================
2   //Copyright 2007 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at 
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.cometd;
16  import java.io.IOException;
17  import java.util.List;
18  
19  import javax.servlet.ServletException;
20  import javax.servlet.http.HttpServletRequest;
21  import javax.servlet.http.HttpServletResponse;
22  
23  import org.mortbay.cometd.AbstractBayeux;
24  import org.mortbay.cometd.AbstractCometdServlet;
25  import org.mortbay.cometd.ClientImpl;
26  import org.mortbay.cometd.MessageImpl;
27  import org.mortbay.cometd.Transport;
28  
29  import dojox.cometd.Extension;
30  import dojox.cometd.Message;
31  
32  public class SuspendingCometdServlet extends AbstractCometdServlet
33  {
34      /* ------------------------------------------------------------ */
35      protected AbstractBayeux newBayeux()
36      {
37          return new SuspendingBayeux();
38      }
39  
40      /* ------------------------------------------------------------ */
41      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
42      {
43          doPost(req,resp);
44      }
45  
46      /* ------------------------------------------------------------ */
47      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
48      {
49          // Look for an existing client and protect from context restarts
50          SuspendingClient client=null;
51          Transport transport=null;
52          boolean connect=false;
53          int num_msgs=-1;
54          
55          // Have we seen this request before
56          boolean initial=false;
57          if (request.isInitial() || request.getAttribute(CLIENT_ATTR)==null)
58          {
59              initial=true;
60              
61              Message[] messages = getMessages(request);
62              num_msgs=messages.length;
63  
64              /* check jsonp parameter */
65              String jsonpParam=request.getParameter("jsonp");
66  
67              // Handle all messages
68              boolean response_pending=false;
69              try
70              {
71                  for (Message message : messages)
72                  {
73                      if (jsonpParam!=null)
74                          message.put("jsonp",jsonpParam);
75  
76                      if (client==null)
77                      {   
78                          client=(SuspendingClient)_bayeux.getClient((String)message.get(AbstractBayeux.CLIENT_FIELD));
79  
80                          // If no client,  SHOULD be a handshake, so force a transport and handle
81                          if (client==null)
82                          {
83                              // Setup a browser ID
84                              String browser_id=browserId(request);
85                              if (browser_id==null)
86                                  browser_id=newBrowserId(request,response);
87  
88                              if (transport==null)
89                              {
90                                  transport=_bayeux.newTransport(client,message);
91                                  transport.setResponse(response);
92                              }
93                              _bayeux.handle(null,transport,message);
94                              message=null;
95  
96                              continue;
97                          }
98                          else
99                          {
100                             String browser_id=browserId(request);
101                             if (browser_id!=null && (client.getBrowserId()==null || !client.getBrowserId().equals(browser_id)))
102                                 client.setBrowserId(browser_id);
103 
104                             // resolve transport
105                             if (transport==null)
106                             {
107                                 transport=_bayeux.newTransport(client,message);
108                                 transport.setResponse(response);
109                             }
110 
111                             // Tell client to hold messages as a response is likely to be sent.
112                             if (!transport.resumePoll())
113                             {
114                                 response_pending=true;
115                                 client.responsePending();
116                             }
117                         }
118                     }
119 
120                     String channel=_bayeux.handle(client,transport,message);
121                     connect|=AbstractBayeux.META_CONNECT.equals(channel);
122                 }
123             }
124             finally
125             {
126                 if (response_pending)
127                     client.responded();
128                 
129                 for (Message message : messages)
130                     ((MessageImpl)message).decRef();
131             }
132         }
133         else
134         {
135             transport=(Transport)request.getAttribute(TRANSPORT_ATTR);
136             transport.setResponse(response);
137             Object clientObj=request.getAttribute(CLIENT_ATTR); 
138             client=(clientObj instanceof ClientImpl)?(SuspendingClient)clientObj:null;
139             if (client!=null)
140                 client.setPollRequest(null);
141         }
142 
143         // Do we need to wait for messages
144         if (transport!=null)
145         {
146             Message pollReply=transport.getPollReply();
147             if (pollReply!=null)
148             {
149                 if (_bayeux.isLogDebug())
150                     _bayeux.logDebug("doPost: transport is polling");
151                 long timeout=client.getTimeout();
152                 if (timeout==0)
153                     timeout=_bayeux.getTimeout();
154 
155                 if (initial)
156                     client.access();
157 
158                 // Get messages or wait
159                 synchronized (client)
160                 {
161                     if (!client.hasMessages() && initial && num_msgs<=1)
162                     {
163                         // save state and suspend
164                         client.setPollRequest(request);
165                         request.setAttribute(CLIENT_ATTR,client);
166                         request.setAttribute(TRANSPORT_ATTR,transport);
167                         request.suspend(timeout);
168                         return;
169                     }
170                 }
171 
172                 for (Extension e:_bayeux.getExtensions())
173                     pollReply=e.sendMeta(pollReply);
174                 transport.send(pollReply);                 
175             }
176             else if (client!=null)
177             {
178                 client.access();
179             }
180         }
181 
182 
183         // Send any messages.
184         if (client!=null) 
185         { 
186             List<Message> messages = null; 
187             Message message = null; 
188             synchronized(client)
189             {
190                 switch (client.getMessages())
191                 {
192                     case 0:
193                         break;
194                     case 1:
195                         message = client.takeMessage(); 
196                         break;
197                     default:
198                         messages = client.takeMessages(); 
199                         break;
200                 }
201                 
202                 if (!_asyncDeliver)
203                 {
204                     try
205                     {
206                         if (message!=null)
207                             transport.send(message); 
208                         else if (messages!=null)
209                             transport.send(messages); 
210                         
211                         transport.complete();
212                         response.flushBuffer();
213 
214                         if (transport.resumePoll())
215                             client.resume();
216                         
217                         return;
218                     }
219                     catch(Throwable e)
220                     {
221                         // delivery failed!
222                         if (message!=null)
223                             client.returnMessage(message);
224                         else if (messages!=null)
225                             client.returnMessages(messages);
226                             
227                         if (e instanceof ServletException)
228                             throw (ServletException)e;
229                         if (e instanceof IOException)
230                             throw (IOException)e;
231                         if (e instanceof RuntimeException)
232                             throw (RuntimeException)e;
233                         if (e instanceof Error)
234                             throw (Error)e;
235                         if (e instanceof ThreadDeath)
236                             throw (ThreadDeath)e;
237                         throw new ServletException(e);
238                     }
239                 }
240             }
241             if (message!=null)
242                 transport.send(message); 
243             else if (messages!=null)
244                 transport.send(messages); 
245             
246             if (transport.resumePoll())
247             	client.resume();
248         }
249         
250         if (transport!=null)
251             transport.complete();
252     }
253 }