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.continuation;
16  
17  import java.io.IOException;
18  import java.nio.ByteBuffer;
19  
20  import javax.servlet.ServletException;
21  import javax.servlet.http.HttpServletRequest;
22  import javax.servlet.http.HttpServletResponse;
23  
24  import org.cometd.Bayeux;
25  import org.cometd.Extension;
26  import org.cometd.Message;
27  import org.mortbay.cometd.AbstractBayeux;
28  import org.mortbay.cometd.AbstractCometdServlet;
29  import org.mortbay.cometd.ClientImpl;
30  import org.mortbay.cometd.JSONTransport;
31  import org.mortbay.cometd.MessageImpl;
32  import org.mortbay.cometd.Transport;
33  import org.mortbay.util.ArrayQueue;
34  import org.mortbay.util.StringUtil;
35  import org.mortbay.util.ajax.Continuation;
36  import org.mortbay.util.ajax.ContinuationSupport;
37  
38  
39  /* ------------------------------------------------------------ */
40  /**
41   * @author gregw
42   * @deprecated use {@link org.mortbay.cometd.SuspendingCometdServlet}
43   */
44  public class ContinuationCometdServlet extends AbstractCometdServlet
45  {
46      /* ------------------------------------------------------------ */
47      @Override
48      protected AbstractBayeux newBayeux()
49      {
50          return new ContinuationBayeux();
51      }
52  
53      /* ------------------------------------------------------------ */
54      @Override
55      protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
56      {
57          // Look for an existing client and protect from context restarts
58          Object clientObj=request.getAttribute(CLIENT_ATTR);
59          ContinuationClient client=(clientObj instanceof ClientImpl)?(ContinuationClient)clientObj:null;
60          Transport transport=null;
61          boolean connect=false;
62          int received=-1;
63          
64          // Have we seen this request before
65          if (client!=null)
66          {
67              // yes - extract saved properties
68              transport=(Transport)request.getAttribute(TRANSPORT_ATTR);
69              transport.setResponse(response);
70          }
71          else
72          {
73              Message[] messages = getMessages(request);
74              received=messages.length;
75  
76              /* check jsonp parameter */
77              String jsonpParam=request.getParameter("jsonp");
78  
79              // Handle all messages
80              try
81              {
82                  for (Message message : messages)
83                  {
84                      if (jsonpParam!=null)
85                          message.put("jsonp",jsonpParam);
86  
87                      if (client==null)
88                      {   
89                          client=(ContinuationClient)_bayeux.getClient((String)message.get(AbstractBayeux.CLIENT_FIELD));
90  
91                          // If no client,  SHOULD be a handshake, so force a transport and handle
92                          if (client==null)
93                          {
94                              // Setup a browser ID
95                              String browser_id=browserId(request);
96                              if (browser_id==null)
97                                  browser_id=newBrowserId(request,response);
98  
99                              if (transport==null)
100                             {
101                                 transport=_bayeux.newTransport(client,message);
102                                 transport.setResponse(response);
103                             }
104                             _bayeux.handle(null,transport,message);
105                             message=null;
106 
107                             continue;
108                         }
109                         else
110                         {
111                             String browser_id=browserId(request);
112                             if (browser_id!=null && (client.getBrowserId()==null || !client.getBrowserId().equals(browser_id)))
113                                 client.setBrowserId(browser_id);
114 
115                             // resolve transport
116                             if (transport==null)
117                             {
118                                 transport=_bayeux.newTransport(client,message);
119                                 transport.setResponse(response);
120                             }
121 
122                             // Tell client to hold messages as a response is likely to be sent.
123                             if (!transport.resumePoll())
124                                 client.responsePending();
125                         }
126                     }
127 
128                     String channel=_bayeux.handle(client,transport,message);
129                     connect|=AbstractBayeux.META_CONNECT.equals(channel);
130                 }
131             }
132             finally
133             {
134                 if (transport!=null && client!=null && !transport.resumePoll())
135                     client.responded();
136                 
137                 for (Message message : messages)
138                     ((MessageImpl)message).decRef();
139             }
140         }
141 
142         Message pollReply=null;
143         // Do we need to wait for messages
144         if (transport!=null)
145         {
146             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                 Continuation continuation=ContinuationSupport.getContinuation(request,client);
156 
157                 // Get messages or wait
158                 synchronized (client)
159                 {
160                     if (!client.hasMessages() && !continuation.isPending()&& received<=1)
161                     {
162                         // save state and suspend
163                         ((ContinuationClient)client).setContinuation(continuation);
164                         request.setAttribute(CLIENT_ATTR,client);
165                         request.setAttribute(TRANSPORT_ATTR,transport);
166                         continuation.suspend(timeout);
167                     }
168                     
169                     if (!continuation.isPending())
170                         client.access();
171 
172                     continuation.reset();
173                 }
174 
175                 ((ContinuationClient)client).setContinuation(null);
176                 transport.setPollReply(null);
177 
178                 for (Extension e:_bayeux.getExtensions())
179                     pollReply=e.sendMeta(pollReply);
180             }
181             else if (client!=null)
182             {
183                 client.access();
184             }
185         }
186 
187         // Send any messages.
188         if (client!=null) 
189         { 
190             
191             synchronized(client)
192             {
193                 client.doDeliverListeners();
194                 ArrayQueue<Message> messages= (ArrayQueue)client.getQueue();
195                 int size=messages.size();
196 
197                 boolean flushed=false;
198                 try
199                 {
200                     if (pollReply!=null)
201                     {
202                         // can we bypass response generation?
203                         if (_refsThreshold>0 && size==1 && transport instanceof JSONTransport)
204                         {
205                             MessageImpl message = (MessageImpl)messages.peek();
206 
207                             // is there a response already prepared
208                             ByteBuffer buffer = message.getBuffer();
209                             if (buffer!=null)
210                             {
211                                 request.setAttribute("org.mortbay.jetty.ResponseBuffer",buffer);
212                                 ((MessageImpl)message).decRef();
213                                 flushed=true;
214 
215                             }
216                             else if (message.getRefs()>=_refsThreshold)
217                             {                                
218                                 byte[] contentBytes = ("[{\""+Bayeux.SUCCESSFUL_FIELD+"\":true,\""+
219                                         Bayeux.CHANNEL_FIELD+"\":\""+Bayeux.META_CONNECT+"\"},"+
220                                         message.getJSON()+"]").getBytes(StringUtil.__UTF8);
221                                 int contentLength = contentBytes.length;
222 
223                                 String headerString = "HTTP/1.1 200 OK\r\n"+
224                                 "Content-Type: text/json; charset=utf-8\r\n" +
225                                 "Content-Length: " + contentLength + "\r\n" +
226                                 "\r\n";
227                                 
228                                 byte[] headerBytes = headerString.getBytes(StringUtil.__UTF8);
229                                 
230                                 buffer = ByteBuffer.allocateDirect(headerBytes.length+contentLength);
231                                 buffer.put(headerBytes);
232                                 buffer.put(contentBytes);
233                                 buffer.flip();
234 
235                                 message.setBuffer(buffer);
236                                 request.setAttribute("org.mortbay.jetty.ResponseBuffer",buffer);
237                                 ((MessageImpl)message).decRef();
238                                 flushed=true;
239                             }
240                             else
241                                 transport.send(pollReply);
242                         }
243                         else
244                             transport.send(pollReply);                        
245                     }
246                     
247                     if (!flushed)
248                     {
249                         Message message = null;
250                         for (int i = 0;i<size;i++)
251                         {
252                             message=messages.getUnsafe(i);
253                             transport.send(message);
254                         }
255 
256                         transport.complete();
257                         flushed=true;
258                     }
259                 }
260                 finally
261                 {
262                     if (flushed)
263                         messages.clear();
264                 }
265             }
266             
267             if (transport.resumePoll())
268             	client.resume();
269         }
270         else if (transport!=null)
271         {
272             transport.complete();
273         }   
274     }
275 }