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.mortbay.demo;
16  
17  import java.io.IOException;
18  import java.io.PrintWriter;
19  import java.net.URLEncoder;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.StringTokenizer;
26  import java.util.concurrent.atomic.AtomicInteger;
27  
28  import javax.servlet.ServletConfig;
29  import javax.servlet.ServletException;
30  import javax.servlet.http.HttpServlet;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.mortbay.jetty.client.ContentExchange;
35  import org.mortbay.jetty.client.HttpClient;
36  import org.mortbay.util.ajax.JSON;
37  
38  /**
39   * Servlet implementation class AsyncRESTServlet.
40   * Enquires ebay REST service for auctions by key word.
41   * May be configured with init parameters: <dl>
42   * <dt>appid</dt><dd>The eBay application ID to use</dd>
43   * </dl>
44   * Each request examines the following request parameters:<dl>
45   * <dt>items</dt><dd>The keyword to search for</dd>
46   * </dl>
47   */
48  public class AsyncRestServlet extends HttpServlet
49  {
50      private final static String __DEFAULT_APPID = "Webtide81-adf4-4f0a-ad58-d91e41bbe85";
51  
52      final static String ITEMS_PARAM = "items";
53      final static String APPID_PARAM = "appid";
54  
55      final static String CLIENT_ATTR = "org.mortbay.demo.client";
56      final static String DURATION_ATTR = "org.mortbay.demo.duration";
57      final static String START_ATTR = "org.mortbay.demo.start";
58  
59      private HttpClient _client;
60      private String _appid;
61  
62      public void init(ServletConfig servletConfig) throws ServletException
63      {
64          if (servletConfig.getInitParameter(APPID_PARAM) == null)
65              _appid = __DEFAULT_APPID;
66          else
67              _appid = servletConfig.getInitParameter(APPID_PARAM);
68  
69          _client = new HttpClient();
70          _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
71  
72          try
73          {
74              _client.start();
75          }
76          catch (Exception e)
77          {
78              throw new ServletException(e);
79          }
80      }
81  
82      protected void doGet(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
83      {
84          Long start=System.currentTimeMillis();
85  
86          if (request.isInitial() || request.getAttribute(CLIENT_ATTR)==null)
87          {
88              // extract keywords to search for
89              String[] keywords=request.getParameter(ITEMS_PARAM).split(",");
90              
91              // define results data structures
92              final List<Map<String, String>> results = 
93                  Collections.synchronizedList(new ArrayList<Map<String, String>>());
94              final AtomicInteger count=new AtomicInteger(keywords.length);
95              
96              // suspend the request
97              request.suspend();
98              request.setAttribute(CLIENT_ATTR, results);
99  
100             // For each keyword
101             for (final String item:keywords)
102             {
103                 // create an asynchronous HTTP exchange
104                 ContentExchange exchange = new ContentExchange()
105                 {
106                     protected void onResponseComplete() throws IOException
107                     {
108                         // extract auctions from the results
109                         Map query = (Map) JSON.parse(this.getResponseContent());
110                         Object[] auctions = (Object[]) query.get("Item");
111                         if (auctions != null)
112                         {
113                             for (Object o : auctions)
114                                 results.add((Map) o);
115                         }
116 
117                         // if that was all, resume the request
118                         if (count.decrementAndGet()<=0)
119                             request.resume();
120                     }
121                 };
122 
123                 // send the exchange
124                 exchange.setMethod("GET");
125                 exchange.setURL("http://open.api.ebay.com/shopping?MaxEntries=5&appid=" + 
126                         _appid + 
127                         "&version=573&siteid=0&callname=FindItems&responseencoding=JSON&QueryKeywords=" + 
128                         URLEncoder.encode(item,"UTF-8"));
129                 _client.send(exchange);
130                 
131             }
132 
133             // save timing info and return
134             request.setAttribute(START_ATTR, start);
135             request.setAttribute(DURATION_ATTR, new Long(System.currentTimeMillis() - start));
136             return;
137         }
138 
139         // resumed request: either we got all the results, or we timed out
140         List<Map<String, String>> results = (List<Map<String, String>>) request.getAttribute(CLIENT_ATTR);
141         response.setContentType("text/html");
142         PrintWriter out = response.getWriter();
143         out.println("<html><head><style type='text/css'>img:hover {height:75px}</style></head><body><small>");
144 
145         for (Map<String, String> m : results)
146         {
147             out.print("<a href=\""+m.get("ViewItemURLForNaturalSearch")+"\">");
148             if (m.containsKey("GalleryURL"))
149                 out.print("<img border='1px' height='20px' src=\""+m.get("GalleryURL")+"\"/>&nbsp;");
150 
151             out.print(m.get("Title"));
152             out.println("</a><br/>");
153         }
154 
155         out.println("<hr />");
156         long duration = (Long) request.getAttribute(DURATION_ATTR);
157         long start0 = (Long) request.getAttribute(START_ATTR);
158 
159         long now = System.currentTimeMillis();
160         out.print("Total Time: ");
161         out.print(now - start0);
162         out.println("ms<br/>");
163         out.print("Thread held: ");
164 
165         long duration2 = now - start;
166         out.print(duration + duration2);
167         out.println("ms (" + duration + " initial + " + duration2 + " resume )");
168         out.println("</small></body></html>");
169         out.close();
170     }
171 
172     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
173     {
174         doGet(request, response);
175     }
176 
177 }