Live stock quote listing using GWT and Spring

In two of the previous posts (XML response, JSON response) I shared my learning about implementing a spring based client application that interacts with yahoo finance API using YQL(Yahoo Query Language), gets finance quotes from the web server for a given symbol.

In this post i would like to share how i use the Google Web Toolkit (GWT) as the front end to display the stock quotes, retrieved by the spring based client application from yahoo finance API. By integrating GWT front end with my spring client application i am showing live stock listing and updates every few minutes for stock symbols entered by the user. Check out my live stock listing application at work that is hosted on google appengine.

Assuming you already have a GWT application with the UI design as per your needs(look at GWT for guidance), the specific steps taken in integrating GWT with the spring application that knows to retrieve stock quotes are shown below:

Step1: Dependency: Add “gwtrpcspring-.jar” to the libraries folder in the project, and make sure CLASSPATH can access this libraries folder. This dependency helps me to use the GWT RPC mechanism to talk to the Spring implementation.

Step2: Define an interface to retrieve stock quotes that extends RemoteService (from GWT) both synchronous and Asynchronous version on GWT client side. The actual implementation of this interface is provided by the spring client application on the GWT server side. Edit the spring client implementation to implement this interface. Also make sure the spring implementation is exported as a service with the “@service” annotation.
Code is shown below with all the required annotations.

Define RPC service to retrieve stock quotes on the GWT Client side

package <packageName>.client
import com.google.gwt.user.client.rpc.RemoteService;

@RemoteServiceRelativePath("stockRetrieve")
public interface StockRetrieveService extends RemoteService{
  Query retrieve(String[] symbols);
}

NOTE: @RemoteServiceRelativePath annotation associates the service identified by “stockRetrieve” with a default path relative to GWT module base URL. Supporting configuration needed for this in web.xml and applicationContext.xml, explained below.

Define Asynchronous service:

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface StockRetrieveServiceAsync {
  void retrieve(String[] symbols, AsyncCallback<Query> callback);
}

NOTE: As part of the GWT client module, the callback procedure has to be implemented for its success and failure scenarios. The sucess scenario will be able to unwrap the Query object to populate the UI with stock quote data.

RPC service implementation to retrieve stock quotes on GWT Server side

package <packageName>.server
@Service
public class StockRetrieveServiceImpl implements StockRetrieveService {
  private final RestTemplate rTemplate;
  private XPathOperations xpathTemplate;
	  
  @Autowired
  StockRetrieveServiceImpl(RestTemplate restTpl) {
    rTemplate = restTpl;  
  }
	  
  @Autowired
  public void setXPathTemplate(XPathOperations xpTemplate) {
    xpathTemplate = xpTemplate;
  }
	
  @Override
  public Query retrieve(String[] symbols) {
  ....
  
  }

Step 3: web.xml in the GWT project:
Edit web.xml in the GWT application to add listener to listen to Spring application context

    <listener>
    org.springframework.web.context.ContextLoaderListener
    </listener>

Edit web.xml in GWT application to define GWT RemoteServiceDispatcher servlet and its servlet mappings. Here the url-pattern shows the GWT module base URL followed by the service

  <!-- Servlets -->
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.gwtrpcspring.RemoteServiceDispatcher</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/stockquotegwt/stockRetrieve</url-pattern> 
  </servlet-mapping> 

Step 4:applicationContext.xml in GWT project:
Edit applicationContext.xml to add the annotation-config and component-scan elements so the annotations will get picked up automatically by spring.

  <context:annotation-config/>
  <context:component-scan base-package="<packageName>.server"/>

Knapsack problem – A Java implementation

Knapsack is a well known problem of packing the knapsack with maximum amount of items within the given weight constraint however of higher value among the available items. The problem description and some of the approaches to solve the problem are laid out in the following wikipedia page “http://en.wikipedia.org/wiki/Knapsack_problem

Here are Java implementation of two of the approaches. To download the source code visit my github account @ https://github.com/sangs/knapsack-ads

Meet in the middle approach to solving 0-1 knapsack problem:

0-1 knapsack problem is where item of each type are either avaiable or not available for packaging. Also, there are not more than one item of each type. By type here i refer to weight, value combination of the item.

void ksZeroOne(int targetWeight, int nItems, int[] iWeights, int[] iValues) {

//Algo: Meet in the middle approach
//Step1: make two lists "s1", "s2" of item ID's
//Step2: Get a list of all subsets of each set "s1", "s2" and
  //maintain one cache each for the two subset lists - "m1", "m2", s.t. only those subsets based on dominance condition
  //(subsets of equal weight however more value) gets into the cache
  //"m1" -- Weight indexed map "m1" of subsets from list "s1". Holds subsets of equal or more value for the given weight and
  //"m2" -- Weight indexed map "m2" of subsets from list "s2". Holds subsets of equal or more value for the given weight
//Step3: For each subset in map1 find corresponding subset in map2 that satisfies
  //the weight criteria however has a highest value so far.
  //Upon finding the items pack it in the "knapsackContents"

itemW = iWeights;
itemV = iValues;
//Items list with maximum value
ArrayList<ArrayList<Integer>> knapsackContents = new ArrayList<ArrayList<Integer>>();
int m = nItems/2;
int remainingWt = 0;

//Step1:
ArrayList<Integer> s1 = new ArrayList<Integer>(); //First half of items IDs
ArrayList<Integer> s2 = new ArrayList<Integer>(); //Second half of item IDs
Map<Integer, ArrayList<ArrayList<Integer>>> m1 = new HashMap<Integer, ArrayList<ArrayList<Integer>>>();
Map<Integer, ArrayList<ArrayList<Integer>>> m2 = new HashMap<Integer, ArrayList<ArrayList<Integer>>>();

for(int i = 0; i < m; i++) {
  s1.add(i);
}
for(int i = m; i <= nItems-1; i++) {
  s2.add(i);
}

//Step2:
ArrayList<ArrayList<Integer>> ld1 = getAllSubsetsByDominance(s1, 0, 0, m1);
ArrayList<ArrayList<Integer>> ld2 = getAllSubsetsByDominance(s2, 0, m, m2);

//Step3:
Set<Map.Entry<Integer, ArrayList<ArrayList<Integer>>>> kvSet1 = m1.entrySet();
Set<Integer> keySet2 = m2.keySet();
ArrayList<Integer> mylst = null;
ArrayList<Integer> blist = null;
int val1 = 0, val2 = 0, maxval = 0;

for(Map.Entry<Integer, ArrayList<ArrayList<Integer>>> elem : kvSet1) {
  val1 = val2 = maxval = 0;
  Integer elemK = elem.getKey();
  ArrayList<Integer> elemVlist = (elem.getValue()).get(0);
  remainingWt = targetWeight-(elem.getKey());
  for(Integer ix : elemVlist) {
    val1 += itemV[ix.intValue()];
  }
  if(!knapsackContents.isEmpty()) {
    for(Integer ix : knapsackContents.get(0)) {
      maxval += itemV[ix.intValue()];
    }
  }
  for(Integer k : keySet2) {
    if(k.intValue() <= remainingWt) {
      ArrayList<Integer> ls = (m2.get(k)).get(0);
      for(Integer ix : ls) {
        val2 += itemV[ix.intValue()];
      }
      //If combined value is higher than what is already chosen to be in knapsack choose this set instead
      if((val1+val2) > maxval) {
        knapsackContents.clear();
        //Get all possible combinations that makes your knapsack rich !!
        for(ArrayList<Integer> l1 : elem.getValue()) {
          mylst = new ArrayList<Integer>();
          mylst.addAll(l1);
          for(ArrayList<Integer> l2 : m2.get(k) ) {
            blist = new ArrayList<Integer>();
            blist.addAll(mylst);
            blist.addAll(l2);
            knapsackContents.add(blist);
          }
        }
      } // List of Maximum value
    } //List with matching wt constraint found in the other half
  } //for
} //for

  //Printout knapsack contents
  System.out.println("Pack the following items in knapsack that is of highest " +
    "total value and within weight constraint of " + targetWeight);

  int tW = 0, tV = 0;
  //Looking at all possible options in the ZERO-ONE knapsack
  for(ArrayList<Integer> l : knapsackContents) {
    System.out.println("Choose items");
    for(Integer e : l) {
      tW += itemW[e];
      tV += itemV[e];
      System.out.println("Weight: " + itemW[e] + ", Value: " + itemV[e]);
    }
    System.out.println("Total weight: " + tW + " Total value: " + tV);
  }
} //ksZeroOne

//offset to indicate which half we are looking at: 0 - first half, m - second half.
ArrayList<ArrayList<Integer>> getAllSubsetsByDominance(ArrayList<Integer> s, int index, int offset, Map<Integer, ArrayList<ArrayList<Integer>>> cache) {
  ArrayList<ArrayList<Integer>> allss;
  if(s.size() == index) {
    allss = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> emptysubset = new ArrayList<Integer>();
    allss.add(emptysubset);
  }
  else {
    allss = getAllSubsetsByDominance(s, index+1, offset, cache);
    ArrayList<ArrayList<Integer>> myallss = new ArrayList<ArrayList<Integer>>();
    int itemId = -1;
    for(ArrayList<Integer> lst : allss) {
      ArrayList<Integer> mysubset = new ArrayList<Integer>(); //itemIDWt
      mysubset.addAll(lst);
      itemId = offset+index;
      mysubset.add(0, itemId);

      //Run dominance procedure starts
      int wt = 0, newval = 0, val = 0;
      if(!mysubset.isEmpty()) { //If not an empty list
      for(Integer i : mysubset) {
        wt += itemW[i];
        newval += itemV[i];
      }

      ArrayList<ArrayList<Integer>> alist;
      if(cache.containsKey(wt)) {
        //OK to get first list as it only holds list of equal value when there is more than one
        //of them of given weight
        alist = cache.get(wt);
        for(Integer i : (alist).get(0)) {
          val += itemV[i];
        }
        if(newval > val) {
          alist.clear();
        }
        if(newval >= val) {
          alist.add(mysubset);
          cache.put(wt, alist);
        }
      }
      else { //Add it if not in the cache
        alist = new ArrayList<ArrayList<Integer>>();
        alist.add(mysubset);
        cache.put(wt, alist);
      }
    }
    //Run dominance procedure ends

    myallss.add(mysubset);
  } //for

  allss.addAll(myallss);
} //else

return allss;
} //getAllSubsetsByDominance

Dynamic programming approach to solving unbounded knapsack problem:

Unbounded knapsack problem is where the number of items of each type can be more than one. Again, by type we refer to the item of a particular weight and value combination.

//Dynamic Programming
void ksUnbounded(int targetWeight, int nItems, int[] iWeights, int[] iValues) {
//Algo: Dynamic Programming/Memoizing
//Step1: Maitain a two-dimensional array, Array[0...nItems][0...targetWeight]
  //Note: Weight increses upto targetWeight
//Step2: Also maintain a ksTrackArray[0...nItems-1][0...targetWeight] to track which items are
  //part of the solution satisfying the weight value constraint
//Step3: At any point in dynamic programming find: maximum combined weight of any subsets of items, o...currentItem
  //of weight atmost iw (the ith weight in the 2-dimensional array)

int[][] ksItemCapacity = new int[nItems+1][targetWeight+1];
int[][] ksTrack = new int[nItems+1][targetWeight+1];

for(int w = 0; w <= targetWeight; w++) {
  ksItemCapacity[0][w] = 0;
}

for(int item = 1; item < nItems; item++) {
  for(int w = 0; w <= targetWeight; w++) {
    //last known Maximum value of KS contents s.t. their weight totals to atmost w-iWeights[item]
    int eItemValue = (iWeights[item] <= w)? ksItemCapacity[item-1][w-iWeights[item]] : 0;
    if( (iWeights[item] <= w) && (iValues[item]+eItemValue) > ksItemCapacity[item-1][w] ) {
      ksItemCapacity[item][w] = eItemValue+iValues[item]; //current item included
      ksTrack[item][w] = 1;
    }
    else {
      ksItemCapacity[item][w] = ksItemCapacity[item-1][w]; //current item not included
      ksTrack[item][w] = 0;
    }
  }
}

//Print KS contents
ArrayList<Integer> ksContents = new ArrayList<Integer>();
int tW = targetWeight;
for(int item = nItems; item >= 0; item--) {
  if(ksTrack[item][tW] == 1) {
    tW -= iWeights[item];
    ksContents.add(item);
  }
}

System.out.println("Items choosen are:");
int W = 0, V = 0;
for(Integer e : ksContents) {
  W += iWeights[e];
  V += iValues[e];
  System.out.println("Weight: " + iWeights[e] + ", Value: " + iValues[e]);
}
System.out.println("Total weight: " + W + " Total value: " + V);
} //ksUnbounded

Parsing XML from Source object using Spring’s XPathTemplate, demo with yahoo finance API client

Spring’s Source (javax.xml.transform.Source) object can handle any of these Source formats: DOMSource, SAXSource and StreamSource.

The following example client interacts with yahoo finance API using YQL(Yahoo Query Language), gets finance quotes data from the web server for a given symbol. The result is retrieved as a Source object and then applies the Spring’s, XPathTemplate provided method to map XML to objects (representing the XML data retrieved). The RestTemplate class provided by Spring is used here which takes care of the request creation and connection establishment details.

Configuration required to use Source and XPathTemplate:

Spring client needs the appropriate message converter to be configured in the applications config file to be able to handle Source objects. In my example i have provided the message converter in the applications root context xml file.


<bean id="rTemplate">
  <property name="messageConverters">
    <list>
      <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
    </list>
  <property/>
<bean/>

<bean id="xpathTemplate" class="org.springframework.xml.xpath.Jaxp13XPathTemplate"/>

Add the following to the Maven dependency in the project

<!-- SourceHttpMessageConverter to convert the HTTP XML response into a javax.xml.transform.Source -->
<dependency>
  <groupId>org.springframework.ws</groupId>
  <artifactId>spring-xml</artifactId>
  <version>2.0.5.RELEASE</version>
</dependency>

The client application is now ready with the dependencies all in place. Here is how the web client would interact with Yahoo’s finance API to get the quotes for a given symbol and retrieve it as a Source object.

private final RestTemplate rTemplate;
private XPathOperations xpathTemplate;
@Autowired
StockQuoteRetrieve(RestTemplate restTpl) {
  rTemplate = restTpl;
}
@Autowired
public void setXPathTemplate(XPathOperations xpTemplate) {
  xpathTemplate = xpTemplate;
}
@RequestMapping(value="/getquotesource", method=RequestMethod.GET)
@ResponseBody
public Source retrieveSource(String requestedSymbol) {
  String env = "store://datatables.org/alltableswithkeys";
  String symbolString = "(" + requestedSymbol + ")";
  String queryStr = "SELECT * from yahoo.finance.quotes where symbol in ";
  String restUrl = "http://query.yahooapis.com/v1/public/yql?q={qid}{symbol}&env={senv}";
  Source sresponse = rTemplate.getForObject(restUrl, Source.class, queryStr,symbolString,env);
  return sresponse;
}

If using google chrome, the data retrieved as a Spring Source object in the above code will be rendered as XML upon return of the response, as the response body contains the XML data in the Source object. However if using IE, please refer to the POST Rendering XML data using ViewResolver and Model.
The XML returned has the following structure.

<query>
  <results>
    <quote symbol="FB">
      <LastTradeDate>7/10/2012</LastTradeDate>
      <PreviousClose>32.17</PreviousClose>
      <Ask>31.74</Ask>
      ....
    </quote>
  </results>
</query>

We will now see how the client application can proceed to parse the Source object in order to map the received XML into objects. The below lines of code explains  how to accomplish that.

import org.springframework.xml.xpath.NodeMapper;
import org.springframework.xml.xpath.XPathOperations;
Query queryObject;
void parseAndProcessSource(Source response) {
  Result rObject = new Result();
  queryObject = new Query();
  List<Quote> qList = xpathTemplate.evaluate("//quote", response, new QUOTENODE_MAPPER());
  rObject.setQuote(qList);
  queryObject.setResult(rObject);
}

Here we are using the xpath expression(//quote) to retrieve the “quote” element from the XML response. QUOTENODE_MAPPER is an implementation of NodeMapper interface. The xpathTemplate uses “NodeMapper” a callback interface, whose mapnode() method is invoked as the xpath expression is encountered in the document. We provide implementation of this callback interface so the callback knows to evaluate our xpath expression in the document. We are extracting the “symbol” attribute of the quote element and all the subelements of the quote element and their contents by providing those implementation for the mapNode(…)  method of the NodeMapper interface.

class QUOTENODE_MAPPER implements NodeMapper<Quote> {
  QUOTENODE_MAPPER() {
  }
  public Quote mapNode(Node node, int nodeNum) throws DOMException {
    Quote quoteObj = new Quote();
    Element quoteElem = (Element) node;
    long longVal = 0;
    quoteObj.setSymbol(quoteElem.getAttribute("symbol"));
    Element askElement = (Element)quoteElem.getElementsByTagName("Ask").item(0);
    if(askElement != null)
      quoteObj.setAsk(askElement.getTextContent());
    Element avgDailyVol = (Element)quoteElem.getElementsByTagName("AverageDailyVolume").item(0);
    if(avgDailyVol != null) {
      longVal = Long.valueOf(avgDailyVol.getTextContent()).longValue();
      quoteObj.setAverageDailyVolume(longVal);
    }
    Element lastTrDate = (Element)quoteElem.getElementsByTagName("LastTradeDate").item(0);
    if(lastTrDate != null)
      quoteObj.setLastTradeDate(lastTrDate.getTextContent());
    Element Open = (Element)quoteElem.getElementsByTagName("Open").item(0);
    if(Open != null)
      quoteObj.setOpen(Open.getTextContent());
    Element PreviousClose = (Element)quoteElem.getElementsByTagName("PreviousClose").item(0);
    quoteObj.setPreviousClose(PreviousClose.getTextContent());
    Element Bid = (Element)quoteElem.getElementsByTagName("Bid").item(0);
    quoteObj.setBid(Bid.getTextContent());

    //Other sub lements are retrieved and mapped to the object
    return quoteObj;

}

}

Retrieving XML response using Spring web client, demo with yahoo finance API client

As i am learning and exploring Spring framework i am sharing the knowledge i am gaining with you.

The steps involved in creating a request with the required format and getting the response in the expected format (XML in this example) is shown below. I am using Spring’s RestTemplate class to create request and retrieve response. To demonstrate this the below web client is issuing a RESTful query to yahoo.finance.quotes YQL(Yahoo Query Language) web server, to get quotes for a given symbol. As you would see below in the code we need to configure a unmarshaller/marshaller to be used by the client web application to convert the XML response into an Object representation (and marshaller for the reverse transformation). There are quite a few marshaller/unmarshaller available. The code below shows usage of 2 of them, JAXB(Java Architecture for XML Binding) and XStream.


@Autowired
RestTemplate rTemplate;

@RequestMapping(value="/getquotex", method=RequestMethod.GET)
public @ResponseBody Query retrievexml(String requestedSymbol) {
  String env = “store://datatables.org/alltableswithkeys”;
  String symbolString = “(” + requestedSymbol + ”)";
  String queryStr = "SELECT * from yahoo.finance.quotes where symbol in ";
  String restUrl = "http://query.yahooapis.com/v1/public/yql?q={qid}{symbol}&env={senv}";
  Query qresponse = rTemplate.getForObject(restUrl, Query.class, queryStr,symbolString,env);
  return qresponse;
}

The above piece of code requries these dependencies to be configured for it to work. Also shown later in this article is the classes that make up the Object that represent the XML data as retrieved from YQL server for the stock quote.

Add the following dependency (Maven dependency shown below) needed to use any OXM (Object to XML mapping) feature in Spring.


<!--Object-to-XML Mapping (OXM) abstraction and integration with JAXB, JiBX, Castor, XStream, and XML Beans  -->

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${org.springframework-version}</version>
</dependency>

//org.springframework-version - is the version of Spring that you are using.

Using JAXB2:

1) Define RestTemplate in the xml file that defines the client applications root application context. It is the xml file at the location “contextConfiguration” as mentioned in the web.xml file.

Marshall and Unmarshall using JAXB2 (Java Architecture for XML Binding)


<bean id="rTemplate">
  <property name="messageConverters">
    <list>
      <bean id="messageConverter">
        <property name="marshaller" ref="jaxb2Marshaller"/>
        <property name="unmarshaller" ref="jaxb2Marshaller"/>
      </bean>
    </list>
  </property>
</bean>

<!-- If using Jaxb2Marshaller as MarshallingHttpMessageConverter -->

<bean id="jaxb2Marshaller"class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
  <property name="classesToBeBound">
    <list>
      <value>com.mytwocents.StockRestWeb.Query</value>
      <value>com.mytwocents.StockRestWeb.Result</value>
      <value>com.mytwocents.StockRestWeb.Quote</value>
    </list>
  </property>
</bean>

2) If using Maven (In STS – SpringSource Tool Suite for development add it to pom.xml)


<!-- JAXB2.x for XML binding -->

<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.3-1</version>
</dependency>

Using XStream:

1) The applications xml file defining the root context will have the below configuration if using XStream for marshalling and unmarshalling.


<bean id="rTemplate">
  <property name="messageConverters">
    <list>
      <bean id="messageConverter">
        <property name="marshaller" ref="xstreamMarshaller"/>
        <property name="unmarshaller" ref="xstreamMarshaller"/>
      </bean>
    </list>
  </property>
</bean>

<!-- If using XStream as MarshallingHttpMessageConverter -->

<bean id="xstreamMarshaller"class="org.springframework.oxm.xstream.XStreamMarshaller">
  <propertyname="annotatedClasses">
    <list>
      <value>com.mytwocents.StockRestWeb.Query</value>
      <value>com.mytwocents.StockRestWeb.Result</value>
      <value>com.mytwocents.StockRestWeb.Quote</value>
    </list>
  </property>
  <propertyname="autodetectAnnotations"value="true"></property>
</bean>

2) If using maven dependencies management


<!-- For HierarchicalStreamReader access in OXM -->

<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.2</version>
</dependency>

The xml returned has the following structure.


<query>
  <results>
    <quote symbol="FB">
      <LastTradeDate>7/10/2012</LastTradeDate>
      <PreviousClose>32.17</PreviousClose>
      <Ask>31.74</Ask>

....

    </quote>
  </results>
</query>

Classes that make up the Object that represent the XML data retrieved from YQL server for the stock quote:

NOTE: The below code shows annotations for both JAXB2 and XStream. However the annotations are active and are parsed only when their usage is configured to be used in the applications root context xml file. Otherwise they are not parsed. So, for example if the application is configured to use JAXB2 then only JAXB2 annotations are parsed and XStreamAnnotations are not parsed. The presence of XStreamAnnotations in that case is harmless in the code (and vice-versa is true as well.)


import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XmlRootElement(name="query")
@XStreamAlias("query")
public class Query {

@XStreamAlias("results")
private Result result;

Query() {

}

@XmlElement(name="results")
public Result getResult() {
  returnresult;
}

public void setResult(Result result) {
  this.result = result;
}

}

import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;

@XmlRootElement(name="results")
@XStreamAlias("results")
public class Result {

@XStreamImplicit(itemFieldName="quote")
private List<Quote> quote;

Result() {

}

@XmlElement
public List<Quote> getQuote() {
  returnquote;
}

publicvoid setQuote(List<Quote> quote) {
  this.quote = quote;
}

}

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;

@XmlRootElement(name="quote")
@XStreamAlias("quote")
public class Quote {

private String Symbol;
private String LastTradeDate;
private String Open;
private String PreviousClose;
private String Ask;

//more fields in the class goes here

Quote() {

}

@XmlAttribute(name="symbol")
public String getSymbol() {
  returnSymbol;
}

publicvoid setSymbol(String symbol) {
  Symbol = symbol;
}

@XmlElement(name="LastTradeDate")
public String getLastTradeDate() {
  returnLastTradeDate;
}

publicvoid setLastTradeDate(String lastTradeDate) {
  LastTradeDate = lastTradeDate;
}

@XmlElement(name="Open")
public String getOpen() {
  return Open;
}

public void setOpen(String open) {
  Open = open;
}

@XmlElement(name="PreviousClose")
public String getPreviousClose() {
  return PreviousClose;
}

public void setPreviousClose(String previousClose) {
  PreviousClose = previousClose;
}

@XmlElement(name="Ask")
public String getAsk() {
  return Ask;
}

public void setAsk(String ask) {
  Ask = ask;
}

//more getters/setters in the class goes here

}

Writing a Spring web client to get JSON response, demo with yahoo finance API client

Using spring framework to write a RESTful Http client is simple and takes only a few lines of code. A lot of the connection establishment and request creation details are done by the RestTemplate class. Below is a demonstration of a web client issuing a RESTful query to yahoo.finance.quotes to get quotes for a given symbol. The below code uses Spring's RestTemplate class to create a request and retrieve response from yahoo's YQL(Yahoo Query Language) web server. I hope some of you reading this post will find it informative.

@Autowired
RestTemplate rTemplate;
@RequestMapping(value=”/getquote”, method=RequestMethod.GET)
public @ResponseBody Wrapper retrieve(String requestedSymbol) {
  String env = “store://datatables.org/alltableswithkeys”;
  String symbolString = “(” + requestedSymbol + ”)";
  String fmt="json";
  String queryStr = “SELECT from yahoo.finance.quotes where symbol in “;
  String restJsonUrl = "http://query.yahooapis.com/v1/public/yql?q={qid}{symbol}&format={fmt}&env={senv}";
  Wrapper response =rTemplate.getForObject(restJsonUrl, Wrapper, queryStr,symbolString,fmt,env);
  return response;
}

Below is an extract of the JSON format result returned by running the above piece of code. I have defined the below classes to map this result returned by the YQL server.

{"query":{"results":{"quote":[{"Symbol":"FB","LastTradeDate":"7/5/2012","Open":"31.30","PreviousClose":"31.20","Ask":"31.07"}]}}}

@JsonIgnoreProperties(ignoreUnknown = true)
public class Wrapper {

  @JsonProperty("query")
  public JResponse rs;

  Wrapper() {      }
}
public class JResponse {

@JsonProperty("results")
public JQuoteHelper jquoteHelp;

//setters/getters

}

class JQuoteHelper {

@JsonProperty("quote")
public List<JQuote> quotes;

//more fields, getters/setters declared here

}

@JsonIgnoreProperties(ignoreUnknown = true)
public class JQuote {

  public String Symbol;
  public String LastTradeDate;
  public String Open;
  public String PreviousClose;
  public String Ask;

//more fields declared here

}

Here are the dependencies for the above code to work with Spring framework.
1) Define rTemplate(RestTemplate) in the xml file that defines the applications root application context. It is the xml file at the "contextConfigLocation" as mentioned in your web.xml file.


<bean id="rTemplate" class="org.springframework.web.client.RestTemplate">
  <property name="messageConverters">
  <list>
    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
  </list>
  </property>
</bean>

2) Declare the following dependency, where ${org.jackson-version} is the version of jackson you use.
<!-- JSON Support -->

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${org.jackson-version}</version>
</dependency>

How to test it:

Startyour webserver http://localhost:port/webappname/getquote

NOTE: Google chrome has a good JSON renderer. If using IE it will prompt download of the JSON response onto your machine.