View Javadoc

1   //========================================================================
2   //Copyright 2004-2008 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.eclipse.jetty.example.asyncrest;
16  
17  import java.io.IOException;
18  import java.io.PrintWriter;
19  import java.util.Map;
20  import java.util.Queue;
21  import java.util.concurrent.ConcurrentLinkedQueue;
22  import java.util.concurrent.atomic.AtomicInteger;
23  
24  import javax.servlet.AsyncContext;
25  import javax.servlet.ServletConfig;
26  import javax.servlet.ServletException;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletResponse;
29  
30  import org.eclipse.jetty.client.ContentExchange;
31  import org.eclipse.jetty.client.HttpClient;
32  import org.eclipse.jetty.util.ajax.JSON;
33  
34  /**
35   * Servlet implementation class AsyncRESTServlet.
36   * Enquires ebay REST service for auctions by key word.
37   * May be configured with init parameters: <dl>
38   * <dt>appid</dt><dd>The eBay application ID to use</dd>
39   * </dl>
40   * Each request examines the following request parameters:<dl>
41   * <dt>items</dt><dd>The keyword to search for</dd>
42   * </dl>
43   */
44  public class AsyncRestServlet extends AbstractRestServlet
45  {
46      final static String RESULTS_ATTR = "org.eclipse.jetty.demo.client";
47      final static String DURATION_ATTR = "org.eclipse.jetty.demo.duration";
48      final static String START_ATTR = "org.eclispe.jetty.demo.start";
49  
50      HttpClient _client;
51  
52      public void init(ServletConfig servletConfig) throws ServletException
53      {
54          super.init(servletConfig);
55          
56          _client = new HttpClient();
57          _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
58  
59          try
60          {
61              _client.start();
62          }
63          catch (Exception e)
64          {
65              throw new ServletException(e);
66          }
67      }
68  
69      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
70      {
71          Long start=System.nanoTime();
72          
73          // Do we have results yet?
74          Queue<Map<String, String>> results = (Queue<Map<String, String>>) request.getAttribute(RESULTS_ATTR);
75          
76          // If no results, this must be the first dispatch, so send the REST request(s)
77          if (results==null)
78          {
79              // define results data structures
80              final Queue<Map<String, String>> resultsQueue = new ConcurrentLinkedQueue<Map<String,String>>();
81              request.setAttribute(RESULTS_ATTR, results=resultsQueue);
82              
83              // suspend the request
84              // This is done before scheduling async handling to avoid race of 
85              // dispatch before startAsync!
86              final AsyncContext async = request.startAsync();
87              async.setTimeout(30000);
88  
89              // extract keywords to search for
90              String[] keywords=request.getParameter(ITEMS_PARAM).split(",");
91              final AtomicInteger outstanding=new AtomicInteger(keywords.length);
92              
93              // Send request each keyword
94              for (final String item:keywords)
95              {
96                  _client.send(
97                          new AsyncRestRequest(item)
98                          {
99                              void onAuctionFound(Map<String,String> auction)
100                             {
101                                 resultsQueue.add(auction);
102                             }
103                             void onComplete()
104                             {
105                                 if (outstanding.decrementAndGet()<=0)
106                                     async.dispatch();
107                             }
108                         });
109             }
110             
111             // save timing info and return
112             request.setAttribute(START_ATTR, start);
113             request.setAttribute(DURATION_ATTR, new Long(System.nanoTime() - start));
114 
115             return;
116         }
117 
118         // We have results!
119         
120         // Generate the response
121         String thumbs = generateThumbs(results);
122         
123         response.setContentType("text/html");
124         PrintWriter out = response.getWriter();
125         out.println("<html><head>");
126         out.println(STYLE);
127         out.println("</head><body><small>");
128 
129         long initial = (Long) request.getAttribute(DURATION_ATTR);
130         long start0 = (Long) request.getAttribute(START_ATTR);
131 
132         long now = System.nanoTime();
133         long total=now-start0;
134         long generate=now-start;
135         long thread=initial+generate;
136         
137         out.print("<b>Asynchronous: "+request.getParameter(ITEMS_PARAM)+"</b><br/>");
138         out.print("Total Time: "+ms(total)+"ms<br/>");
139 
140         out.print("Thread held (<span class='red'>red</span>): "+ms(thread)+"ms (" + ms(initial) + " initial + " + ms(generate) + " generate )<br/>");
141         out.print("Async wait (<span class='green'>green</span>): "+ms(total-thread)+"ms<br/>");
142         
143         out.println("<img border='0px' src='asyncrest/red.png'   height='20px' width='"+width(initial)+"px'>"+
144                     "<img border='0px' src='asyncrest/green.png' height='20px' width='"+width(total-thread)+"px'>"+
145                     "<img border='0px' src='asyncrest/red.png'   height='20px' width='"+width(generate)+"px'>");
146         
147         out.println("<hr />");
148         out.println(thumbs);
149         out.println("</small>");
150         out.println("</body></html>");
151         out.close();
152     }
153 
154     private abstract class AsyncRestRequest extends ContentExchange
155     {
156         AsyncRestRequest(final String item)
157         {
158             // send the exchange
159             setMethod("GET");
160             setURL(restURL(item));   
161         }
162         
163         abstract void onAuctionFound(Map<String,String> details);
164         abstract void onComplete();
165         
166         protected void onResponseComplete() throws IOException
167         {
168             // extract auctions from the results
169             Map<String,?> query = (Map<String,?>) JSON.parse(this.getResponseContent());
170             Object[] auctions = (Object[]) query.get("Item");
171             if (auctions != null)
172             {
173                 for (Object o : auctions)
174                     onAuctionFound((Map<String,String>)o);
175             }
176 
177             onComplete();
178         }
179 
180         /* ------------------------------------------------------------ */
181         protected void onConnectionFailed(Throwable ex)
182         {
183             getServletContext().log("onConnectionFailed: ",ex);
184             onComplete();
185         }
186 
187         /* ------------------------------------------------------------ */
188         protected void onException(Throwable ex)
189         {
190             getServletContext().log("onConnectionFailed: ",ex);
191             onComplete();
192         }
193 
194         /* ------------------------------------------------------------ */
195         protected void onExpire()
196         {
197             getServletContext().log("onConnectionFailed: expired");
198             onComplete();
199         }
200     }
201 
202     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
203     {
204         doGet(request, response);
205     }
206 
207 }