//========================================================================
//Copyright 2004-2008 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================
package org.mortbay.demo;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.util.ajax.JSON;
/**
* Servlet implementation class AsyncRESTServlet.
* Enquires ebay REST service for auctions by key word.
* May be configured with init parameters: <dl>
* <dt>appid</dt><dd>The eBay application ID to use</dd>
* </dl>
* Each request examines the following request parameters:<dl>
* <dt>items</dt><dd>The keyword to search for</dd>
* </dl>
*/
public class AsyncRestServlet extends AbstractRestServlet
{
/**
*
*/
private static final long serialVersionUID = -552089975952264828L;
final static String RESULTS_ATTR = "org.mortbay.demo.client";
final static String DURATION_ATTR = "org.mortbay.demo.duration";
final static String START_ATTR = "org.mortbay.demo.start";
HttpClient _client;
public void init(ServletConfig servletConfig) throws ServletException
{
super.init(servletConfig);
_client = new HttpClient();
_client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
try
{
_client.start();
}
catch (Exception e)
{
throw new ServletException(e);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Long start=System.nanoTime();
// resumed request: either we got all the results, or we timed out
@SuppressWarnings("unchecked")
Queue<Map<String, String>> results = (Queue<Map<String, String>>) request.getAttribute(RESULTS_ATTR);
if (results==null)
{
// define results data structures
results = new ConcurrentLinkedQueue<Map<String,String>>();
request.setAttribute(RESULTS_ATTR, results);
// suspend the request
// This is done before scheduling async handling to avoid race of
// resume before suspend!
final Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.setTimeout(10000);
continuation.suspend();
// extract keywords to search for
String[] keywords=request.getParameter(ITEMS_PARAM).split(",");
final AtomicInteger count=new AtomicInteger(keywords.length);
// Send request each keyword
for (final String item:keywords)
{
sendAsyncRestRequest(item,results,count,continuation/*,debug*/);
}
// save timing info and return
request.setAttribute(START_ATTR, start);
request.setAttribute(DURATION_ATTR, new Long(System.nanoTime() - start));
return;
}
// Generate the response
String thumbs = generateThumbs(results);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><head>");
out.println(STYLE);
out.println("</head><body><small>");
long initial = (Long) request.getAttribute(DURATION_ATTR);
long start0 = (Long) request.getAttribute(START_ATTR);
long now = System.nanoTime();
long total=now-start0;
long generate=now-start;
long thread=initial+generate;
out.print("<b>Asynchronous: "+request.getParameter(ITEMS_PARAM)+"</b><br/>");
out.print("Total Time: "+ms(total)+"ms<br/>");
out.print("Thread held (<span class='red'>red</span>): "+ms(thread)+"ms (" + ms(initial) + " initial + " + ms(generate) + " generate )<br/>");
out.print("Async wait (<span class='green'>green</span>): "+ms(total-thread)+"ms<br/>");
out.println("<img border='0px' src='red.png' height='20px' width='"+width(initial)+"px'>"+
"<img border='0px' src='green.png' height='20px' width='"+width(total-thread)+"px'>"+
"<img border='0px' src='red.png' height='20px' width='"+width(generate)+"px'>");
out.println("<hr />");
out.println(thumbs);
out.println("</small>");
out.println("</body></html>");
out.close();
}
private void sendAsyncRestRequest(
final String item,
final Queue<Map<String, String>> results,
final AtomicInteger count,
final Continuation continuation /*,
final StringBuffer debug*/)
throws IOException
{
// create an asynchronous HTTP exchange
ContentExchange exchange = new ContentExchange()
{
@SuppressWarnings("unchecked")
protected void onResponseComplete() throws IOException
{
// extract auctions from the results
Map<String, Object[]> query = (Map<String, Object[]>) JSON.parse(this.getResponseContent());
Object[] auctions = (Object[]) query.get("Item");
if (auctions != null)
{
for (Object o : auctions)
results.add((Map<String, String>) o);
}
doCount();
}
/* ------------------------------------------------------------ */
protected void onConnectionFailed(Throwable ex)
{
doCount();
}
/* ------------------------------------------------------------ */
protected void onException(Throwable ex)
{
doCount();
}
/* ------------------------------------------------------------ */
protected void onExpire()
{
doCount();
}
/* ------------------------------------------------------------ */
private void doCount()
{
if (count.decrementAndGet()<=0)
{
// debug.append("got & resume "+item+"\n");
continuation.resume();
}
else
{
// debug.append("got "+item+"\n");
}
}
};
// send the exchange
exchange.setMethod("GET");
exchange.setURL(restURL(item));
_client.send(exchange);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doGet(request, response);
}
}