View Javadoc

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.Client;
26  import org.cometd.Extension;
27  import org.cometd.Message;
28  import org.mortbay.cometd.AbstractBayeux;
29  import org.mortbay.cometd.AbstractCometdServlet;
30  import org.mortbay.cometd.ClientImpl;
31  import org.mortbay.cometd.JSONTransport;
32  import org.mortbay.cometd.MessageImpl;
33  import org.mortbay.cometd.Transport;
34  import org.mortbay.util.ArrayQueue;
35  import org.mortbay.util.StringUtil;
36  import org.mortbay.util.ajax.Continuation;
37  import org.mortbay.util.ajax.ContinuationSupport;
38  
39  
40  public class ContinuationCometdServlet extends AbstractCometdServlet
41  {
42      /* ------------------------------------------------------------ */
43      @Override
44      protected AbstractBayeux newBayeux()
45      {
46          return new ContinuationBayeux();
47      }
48  
49      /* ------------------------------------------------------------ */
50      @Override
51      protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
52      {
53          // Look for an existing client and protect from context restarts
54          Object clientObj=request.getAttribute(CLIENT_ATTR);
55          Transport transport=null;
56          int received=-1;
57          boolean metaConnectDeliveryOnly=false;
58          boolean pendingResponse=false;
59          boolean metaConnect=false;
60  
61          // Have we seen this request before
62          ContinuationClient client=(clientObj instanceof ClientImpl)?(ContinuationClient)clientObj:null;
63          if (client!=null)
64          {
65              // yes - extract saved properties
66              transport=(Transport)request.getAttribute(TRANSPORT_ATTR);
67              transport.setResponse(response);
68              metaConnectDeliveryOnly=client.isMetaConnectDeliveryOnly() || transport.isMetaConnectDeliveryOnly();
69              metaConnect=true;
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 received 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=findBrowserId(request);
96                              if (browser_id==null)
97                                  browser_id=setBrowserId(request,response);
98  
99                              if (transport==null)
100                             {
101                                 transport=_bayeux.newTransport(client,message);
102                                 transport.setResponse(response);
103                                 metaConnectDeliveryOnly=transport.isMetaConnectDeliveryOnly();
104                             }
105                             _bayeux.handle(null,transport,message);
106                             message=null;
107                             continue;
108                         }
109                     }
110                     
111                     String browser_id=findBrowserId(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                         metaConnectDeliveryOnly=client.isMetaConnectDeliveryOnly() || transport.isMetaConnectDeliveryOnly();
121                     }
122 
123                     // Tell client to hold messages as a response is likely to be sent.
124                     if (!metaConnectDeliveryOnly && !pendingResponse)
125                     {
126                         pendingResponse=true;
127                         client.responsePending();
128                     }
129                     
130                     if (Bayeux.META_CONNECT.equals(message.getChannel()))
131                         metaConnect=true;
132                     
133                     _bayeux.handle(client,transport,message);
134                 }
135             }
136             finally
137             {
138                 for (Message message : messages)
139                     ((MessageImpl)message).decRef();
140                 if (pendingResponse)
141                 {
142                     client.responded();
143                 }
144             }
145         }
146 
147         Message metaConnectReply=null;
148         // Do we need to wait for messages
149         if (transport!=null)
150         {
151             metaConnectReply=transport.getMetaConnectReply();
152             if (metaConnectReply!=null)
153             {
154                 long timeout=client.getTimeout();
155                 if (timeout==0)
156                     timeout=_bayeux.getTimeout();
157 
158                 Continuation continuation=ContinuationSupport.getContinuation(request,client);
159 
160                 // Get messages or wait
161                 synchronized (client)
162                 {
163                     if (!client.hasMessages() && !continuation.isPending()&& received<=1)
164                     {
165                         // save state and suspend
166                         ((ContinuationClient)client).setContinuation(continuation);
167                         request.setAttribute(CLIENT_ATTR,client);
168                         request.setAttribute(TRANSPORT_ATTR,transport);
169                         continuation.suspend(timeout);
170                     }
171 
172                     if (!continuation.isPending())
173                         client.access();
174 
175                     continuation.reset();
176                 }
177 
178                 ((ContinuationClient)client).setContinuation(null);
179                 transport.setMetaConnnectReply(null);
180 
181             }
182             else if (client!=null)
183             {
184                 client.access();
185             }
186         }
187 
188         // Send any messages.
189         if (client!=null && (!metaConnectDeliveryOnly || metaConnect))
190         {
191             synchronized(client)
192             {
193                 client.doDeliverListeners();
194 
195                 final ArrayQueue<Message> messages= (ArrayQueue)client.getQueue();
196                 final int size=messages.size();           
197 
198                 try
199                 {
200                     for (int i=0;i<size;i++)
201                     {
202                         final Message message=messages.getUnsafe(i);
203                         final MessageImpl mesgImpl=(message instanceof MessageImpl)?(MessageImpl)message:null;
204 
205                         // Can we short cut the message?
206                         if (i==0 && size==1 && mesgImpl!=null && _refsThreshold>0 && metaConnectReply!=null && transport instanceof JSONTransport)
207                         {
208                             // is there a response already prepared
209                             ByteBuffer buffer = mesgImpl.getBuffer();
210                             if (buffer!=null)
211                             {
212                                 // Send pre-prepared buffer
213                                 request.setAttribute("org.mortbay.jetty.ResponseBuffer",buffer);
214                                 if (metaConnectReply instanceof MessageImpl)
215                                     ((MessageImpl)metaConnectReply).decRef();
216                                 metaConnectReply=null;
217                                 transport=null;
218                                 mesgImpl.decRef();
219                                 continue;
220                             }
221                             else if (mesgImpl.getRefs()>=_refsThreshold)
222                             {
223                                 // create multi-use buffer
224                                 byte[] contentBytes = ("["+mesgImpl.getJSON()+",{\""+Bayeux.SUCCESSFUL_FIELD+"\":true,\""+
225                                         Bayeux.CHANNEL_FIELD+"\":\""+Bayeux.META_CONNECT+"\"}]")
226                                         .getBytes(StringUtil.__UTF8);
227                                 int contentLength = contentBytes.length;
228 
229                                 String headerString = "HTTP/1.1 200 OK\r\n"+
230                                 "Content-Type: text/json; charset=utf-8\r\n" +
231                                 "Content-Length: " + contentLength + "\r\n" +
232                                 "\r\n";
233 
234                                 byte[] headerBytes = headerString.getBytes(StringUtil.__UTF8);
235 
236                                 buffer = ByteBuffer.allocateDirect(headerBytes.length+contentLength);
237                                 buffer.put(headerBytes);
238                                 buffer.put(contentBytes);
239                                 buffer.flip();
240 
241                                 mesgImpl.setBuffer(buffer);
242                                 request.setAttribute("org.mortbay.jetty.ResponseBuffer",buffer);
243                                 metaConnectReply=null;
244                                 if (metaConnectReply instanceof MessageImpl)
245                                     ((MessageImpl)metaConnectReply).decRef();
246                                 transport=null;
247                                 mesgImpl.decRef();
248                                 continue;
249                             }
250                         }
251 
252                         if (message!=null)
253                             transport.send(message);
254                         if (mesgImpl!=null)
255                             mesgImpl.decRef();
256                     }
257                 }
258                 finally
259                 {
260                     messages.clear();
261                 }
262             }
263 
264             if (metaConnectReply!=null)
265             {
266                 metaConnectReply=_bayeux.extendSendMeta(client,metaConnectReply);
267                 transport.send(metaConnectReply);
268                 if (metaConnectReply instanceof MessageImpl)
269                     ((MessageImpl)metaConnectReply).decRef();
270             }
271         }
272         
273         if (transport!=null)
274             transport.complete();
275     }
276 }