{"id":416,"date":"2011-07-26T23:46:42","date_gmt":"2011-07-26T22:46:42","guid":{"rendered":"http:\/\/www.xenonique.co.uk\/blog\/?p=416"},"modified":"2011-07-29T23:54:58","modified_gmt":"2011-07-29T22:54:58","slug":"scalafication-of-an-equity-quote-application-part-3","status":"publish","type":"post","link":"https:\/\/www.xenonique.co.uk\/blog\/2011\/07\/26\/scalafication-of-an-equity-quote-application-part-3\/","title":{"rendered":"Scalafication of an Equity Quote Application Part 3"},"content":{"rendered":"<p>This is the final part in a series of article about porting an existing Java application to Scala. The application calls the <a href=\"https:\/\/developer.yahoo.com\/finance\/company.html\" title=\"Yahoo! Finance Web Service \"><strong>Yahoo! Financial web service<\/strong><\/a> and retrieve stock quotes for the user, storing them in a local repository. The original Java project was described in the <a href=\"https:\/\/www.xenonique.co.uk\/blog\/?p=371\" title=\"Scalafication of a Java Equity Quote Application Part 1\" target=\"_blank\">part one<\/a>.<\/p>\n<p>In the <a href=\"https:\/\/www.xenonique.co.uk\/blog\/?p=380\" title=\"Scalafication of an Equity Quote Application Part 2\" target=\"_blank\">part two<\/a> of this series, we looked at the unit test, and in particular the behaviour driven development (BDD) styles in the ScalaTest framework.<\/p>\n<p>We will look at the Scala implementation of this business application. The easiest task is to simply convert the exceptions. Here are the <code>Exceptions.scala<\/code> file:<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: scala; title: ; notranslate\" title=\"\">\r\npackage uk.co.xenonique.stockquoteapp_scala\r\n\r\nclass ConnectionException( msg: String, root: Exception ) \r\nextends RuntimeException( msg, root ) {\r\n   def this( msg: String ) = this( msg, null )\r\n}\r\n\r\nclass DataRetrievalException( msg: String, root: Exception ) \r\nextends RuntimeException( msg, root ) {\r\n   def this( msg: String ) = this( msg, null )\r\n}\r\n\r\nclass UnknownStockSymbolException( msg: String, root: Exception ) \r\nextends RuntimeException( msg, root ) {\r\n   def this( msg: String ) = this( msg, null )\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>We have more than one public class in a single source file. It is sort of allow Scala to look like a dynamic scripting language like Python or Ruby.<\/p>\n<p>Next, we port the quote repository over to Scala. We remove the semi-colons, reverse the declarations of the variable types on the way. We take advantage of Scala support for <code>Option<\/code> types, we retrieve the stock quote by symbol name. The Scala library has two types of collections: <em>immutable<\/em> and <em>mutable<\/em>. By default, immutable collections are those that imported into the compiler as a predefinition. The <code>QuoteRepositoryKeyValueStore <\/code> imports a mutable <code>Map <\/code>collection for storing symbol associated with stock quotes.\u00a0<\/p>\n<p>Here is the <code>QuoteRepository.scala<\/code> file:<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: scala; title: ; notranslate\" title=\"\">\r\npackage uk.co.xenonique.stockquoteapp_scala\r\n\r\nimport scala.collection.mutable.Map\r\n\r\ntrait QuoteRepository {\r\n  def isEmpty(): Boolean\r\n  def size(): Int\r\n  def clear(): Unit\r\n  def store( quote: StockQuote  ): Unit\r\n  def get( symbol: String ): Option[StockQuote] \r\n  def contains( symbol: String ): Boolean\r\n  def getSymbols(): List[String]\r\n}\r\n\r\nclass QuoteRepositoryKeyValueStore extends QuoteRepository {\r\n\r\n  private var store: Map[String, StockQuote] = Map()\r\n\r\n  def clear(): Unit = { store.clear() }\r\n\r\n  def contains(symbol: String): Boolean = store.contains(symbol)\r\n\r\n  def get(symbol: String ): Option[StockQuote] = store.get(symbol)\r\n\r\n  def isEmpty(): Boolean = store.isEmpty\r\n\t\r\n  def size(): Int = store.size\r\n\r\n  def store(quote: StockQuote ): Unit = {\r\n    store.put(quote.symbol, quote)\r\n  }\r\n\r\n  def getSymbols(): List[String] = {\r\n    \/\/ We use a tuple to iterate through the map, which return mutable list\r\n    \/\/ then we use &quot;toList&quot; to convert to an immutable list.\r\n    ( for ( (key,value ) &lt;- store ) yield (key)  ).toList\r\n  }\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>In above implementation class, we can write, in Scala, methods such as <code>size()<\/code> as one-liner methods. <\/p>\n<p>We can convert the Java stock quotes to a <strong>case class<\/strong> and get the benefits of an implicit <code>equalsTo()<\/code>, <code>hashCode()<\/code> and <code>toString()<\/code> methods; and even get a handy <code>copy()<\/code> method too. Note: That we override the definition of the <code>toString()<\/code> class specifically for better formatting and, of course, debugging, but we did not need to do this for a case class.<\/p>\n<p>Here is the <code>StockQuote.scala<\/code> file:<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: scala; title: ; notranslate\" title=\"\">\r\npackage uk.co.xenonique.stockquoteapp_scala\r\n\r\nimport java.math.RoundingMode\r\nimport java.math.BigDecimal\r\n\r\ncase class StockQuote( symbol: String, askPrice: BigDecimal, bidPrice: BigDecimal ) {\r\n\r\n  \/\/ Auxiliary (convenience) constructor\r\n  def this( symbol: String, askPriceStr: String, bidPriceStr: String ) =\r\n    this ( symbol, new BigDecimal(askPriceStr).setScale(2, RoundingMode.DOWN ), new BigDecimal(bidPriceStr).setScale(2, RoundingMode.DOWN ) )\r\n\r\n  \/\/ Auxiliary (copy) constructor\r\n  def this( ref: StockQuote ) = this ( ref.symbol, ref.askPrice, ref.bidPrice )\r\n\r\n  def meanPrice: BigDecimal = bidPrice.add( askPrice.subtract(bidPrice).divide( StockQuote.TWO ).setScale(2, RoundingMode.DOWN ) )\r\n\r\n  override def toString(): String = {\r\n    return &quot;StockQuote [ symbol=&quot; + symbol +&quot;, askPrice=&quot; + askPrice +\r\n      &quot;, bidPrice=&quot; + bidPrice + &quot;, meanPrice=&quot;+meanPrice+&quot;]&quot;;\r\n  }\r\n}\r\n\r\nobject StockQuote {\r\n  def TWO = new BigDecimal(&quot;2.0&quot;)\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>The real hard part of the quote retriever service is get information from the Internet, by calling the web service. Luckily calling the Yahoo! Finance service is a simple REST GET request in comparison to other more complicated SOAP or REST web services.<\/p>\n<p>Here is the <code>QuoteRetriever.scala<\/code> file:<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: scala; title: ; notranslate\" title=\"\">\r\npackage uk.co.xenonique.stockquoteapp_scala\r\n\r\nimport java.lang.NumberFormatException\r\nimport java.io._\r\nimport java.net._\r\n\r\ntrait QuoteRetriever {\r\n  def getStockQuote( symbol: String ): StockQuote\r\n}\r\n\r\nobject QuoteRetrieverYahooImpl {\r\n   val TOKEN_SYMBOL=&quot;@SYMBOL@&quot;\r\n   val DEFAULT_URL_FRAGMENT=&quot;https:\/\/finance.yahoo.com\/d\/quotes.csv?s=&quot;+TOKEN_SYMBOL+&quot;&amp;f=sb2b3&quot;\r\n\r\n}\r\n\r\nclass QuoteRetrieverYahooImpl( var urlFragment: String = QuoteRetrieverYahooImpl.DEFAULT_URL_FRAGMENT ) \r\nextends QuoteRetriever {\r\n  \r\n  def getStockQuote( symbol: String ): StockQuote = {\r\n\r\n    val queryUrl: String = urlFragment.replace(QuoteRetrieverYahooImpl .TOKEN_SYMBOL, symbol)\r\n\r\n    try {\r\n      val url: URL = new URL( queryUrl )\r\n      val connection: HttpURLConnection  = url.openConnection().asInstanceOf[HttpURLConnection]\r\n      connection.setRequestMethod(&quot;GET&quot;)\r\n      connection.connect()\r\n\r\n      val is: InputStream = connection.getInputStream()\r\n      val reader: BufferedReader = new BufferedReader( new InputStreamReader(is) )\r\n\r\n      try {\r\n        val line = reader.readLine()\r\n        if ( line != null ) {\r\n          val tokens = line.split(&quot;,&quot;).toList\r\n          if ( tokens.length &lt; 3 ) {\r\n            throw new DataRetrievalException(\r\n              &quot;Data service at URL=[&quot;+queryUrl+&quot;] does not supply enough fields (3 != &quot;+tokens.length+&quot;)&quot;)\r\n          }\r\n          var stockSymbol = tokens(0)\r\n          if ( stockSymbol.length() &gt; 0 &amp;&amp;\r\n              stockSymbol.charAt(0) == '&quot;' &amp;&amp; stockSymbol.charAt(stockSymbol.length()-1) == '&quot;') {\r\n            stockSymbol = stockSymbol.substring(1, stockSymbol.length()-1 )\r\n          }\r\n          val askPrice = tokens(1)\r\n          if ( &quot;N\/A&quot;.equals(askPrice)) {\r\n            throw new UnknownStockSymbolException(\r\n              &quot;Unknown stock quote symbol [&quot;+stockSymbol+&quot;] price not available&quot;)\r\n          }\r\n          val bidPrice = tokens(2)\r\n          if ( &quot;N\/A&quot;.equals(bidPrice)) {\r\n            throw new UnknownStockSymbolException(\r\n              &quot;Unknown stock quote symbol [&quot;+stockSymbol+&quot;] price not available&quot;)\r\n          }\r\n\r\n          try {\r\n            return new StockQuote(stockSymbol, askPrice, bidPrice)\r\n          }\r\n          catch {\r\n            case e:NumberFormatException =&gt;\r\n              throw new DataRetrievalException(\r\n                &quot;Unable to read numerical data from quote service URL=[&quot;+queryUrl+&quot;]&quot;, e)\r\n          }\r\n        }\r\n        else {\r\n          throw new DataRetrievalException(\r\n            &quot;Data unavailable from quote service URL=[&quot;+queryUrl+&quot;]&quot;)\r\n        }\r\n      }\r\n      finally  {\r\n        if ( reader != null) {\r\n          try {\r\n            reader.close()\r\n          }\r\n          catch  {\r\n            case e: IOException =&gt; null\r\n          }\r\n        }\r\n        if ( is != null) {\r\n          try {\r\n            is.close()\r\n          } \r\n          catch {\r\n            case e: IOException =&gt; null\r\n          }\r\n        }\r\n      }\r\n    }\r\n    catch {\r\n      case e: MalformedURLException  =&gt;\r\n        throw new ConnectionException(&quot;unable to connect to the remote quote service URL=[&quot;+queryUrl+&quot;]&quot;, e)\r\n\r\n      case e: IOException =&gt;\r\n        throw new ConnectionException(&quot;I\/O failure reading service URL=[&quot;+queryUrl+&quot;]&quot;, e)\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Effectively, we could have left the <code>QuoteRetriever<\/code> as a simple Java interface, because we gained nothing. If however we wanted to define a default implementation in a Scala trait we could do so, where it would be not possible with the current version of Java 6 \/ 7.<\/p>\n<p>We make use of the companion object <code>QuoteRetrieverYahooImpl<\/code> to define Scala constants. Essentially this would be same as declaring a Java variable as <code>public final static String<\/code>.<\/p>\n<p>Inside the <code>QuoteRetrieverYahooImpl<\/code> class, let us look at the implementation method <code>getStockQuote()<\/code>. We chucked away Java standard library <code>StringTokenizer<\/code> and replaced it with the nicer Scala equivalent. We split a String into <code>Array[String]<\/code> of fragments using the <code>split<\/code> method, and then convert the array into a list collection by calling <code>toList<\/code>. Since Scala 2.8, these methods <code>toList()<\/code>, <code>toSet()<\/code> and <code>toArray()<\/code> are ubiquitous across the Scala collections.<\/p>\n<p>There is probably a more object functional way of building the temporary variables using functions and closures, however for intermediate Java developer this is easier to follow. The rest of the code is just boiler plate to tackle the exception handling of the Java I\/O library. See the last part of this article for an improvement.<\/p>\n<p>Last, but not least is the main application class in the <code>StockQuoteApp.scala<\/code> file.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: scala; title: ; notranslate\" title=\"\">\r\npackage uk.co.xenonique.stockquoteapp_scala\r\n\r\nimport java.lang.System\r\n\r\nclass StockQuoteApp( retriever: QuoteRetriever, repo: QuoteRepository ) {\r\n  \r\n\r\n  def this() = this( new QuoteRetrieverYahooImpl(), new QuoteRepositoryKeyValueStore() )\r\n\r\n  def doInteractive(): Unit = {\r\n\r\n    println(&quot;=======================================================&quot;)\r\n    println(&quot;   Welcome Peter Pilgrim's Stock Quote Application&quot;)\r\n    println(&quot;=======================================================&quot;)\r\n\r\n    var quit = false\r\n    while ( !quit) {\r\n      println(&quot;Type in a stock symbol or `:repo' to list the repository or `:quit' to quit&quot;)\r\n      println(&quot;or `:clear' to clear the repository&quot;)\r\n      var line = readLine(&quot;$ &quot;)\r\n      if ( line != &quot;&quot; ) {\r\n        line = line.trim()\r\n        if ( line.startsWith(&quot;:r&quot;) || line.startsWith(&quot;:p&quot;)) {\r\n          val symbols  = repo.getSymbols()\r\n          printf(&quot;%10s  %9s %9s %9s\\n&quot;, &quot;SYMBOL&quot;, &quot;MEAN&quot;, &quot;ASK&quot;, &quot;BID&quot;)\r\n          printf(&quot;%10s  %9s %9s %9s\\n&quot;, &quot;--------&quot;, &quot;------&quot;, &quot;-----&quot;, &quot;-----&quot;)\r\n          for ( symbol &lt;- symbols ) {\r\n            val quote = repo.get(symbol).get\r\n            printf(&quot;%10s: %9.2f %9.2f %9.2f\\n&quot;, quote.symbol, quote.meanPrice, quote.askPrice, quote.bidPrice )\r\n          }\r\n        }\r\n        else if ( line.startsWith(&quot;:c&quot;)) {\r\n          repo.clear()\r\n          println(&quot;Repository cleared.&quot;)\r\n        }\r\n        else if ( line.startsWith(&quot;:q&quot;)) {\r\n          quit = true\r\n        }\r\n        else {\r\n          if ( line.length() &gt; 0 ) {\r\n            try {\r\n              val quote = findAndUpdateStockQuote(line)\r\n              printf(&quot;%s: %7.2f\\n&quot;, quote.symbol, quote.meanPrice)\r\n            }\r\n            catch {\r\n              case e:ConnectionException =&gt;\r\n                System.err.println(&quot;CONNECTION ERROR : &quot;+e.getMessage())\r\n\r\n              case e: UnknownStockSymbolException =&gt;\r\n                System.err.println(&quot;STOCK SYMBOL NOT FOUND : &quot;+e.getMessage())\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    \/\/ Prints name and age to the console\r\n    println(&quot;Goodbye.&quot;)\r\n  }\r\n\r\n\r\n  def  findAndUpdateStockQuote(symbol: String): StockQuote = {\r\n    val quote = retriever.getStockQuote(symbol)\r\n    repo.store(quote)\r\n    quote\r\n  }\r\n\r\n}\r\n\r\nobject StockQuoteApp {\r\n  val CONNECTION_PROPERTIES_FILENAME: String = &quot;connection.properties&quot;\r\n\r\n  def main( args: Array[String] ): Unit = {\r\n    println(&quot;Hello world. This is the Scala application!&quot;)\r\n    new StockQuoteApp().doInteractive\r\n  }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>It should be fairly self-explanatory that this program is interactive one. It reads a symbol at the command line and invokes the quote retriever to find the stock quote for the symbol.<\/p>\n<p>There is one other improvement to <code>QuoteRetrieverYahooImpl<\/code> we could make use of. Scala supports function objects; functions calling other functions and returning a different (or the same input) function. Scala methods on class or object types can have more than one parameter list in order to support <em>currying<\/em> of parameters. Scala language has the feature of partial functions that enable this facility.<\/p>\n<p>Here is a class called <code>TryResource<\/code>, which closes an <code>InputStream<\/code> resource.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: scala; title: ; notranslate\" title=\"\">\r\npackage uk.co.xenonique.stockquoteapp_scala\r\n\r\nimport java.io.InputStream\r\nimport java.io.FileInputStream\r\nimport java.io.InputStreamReader\r\nimport java.io.BufferedReader\r\nimport java.io.IOException\r\nimport java.lang.System\r\n\r\n\/\/ Needs to be generic\r\nclass TryResource[T &lt;: InputStream]( inputStream: T ) {\r\n\r\n  private val is: T = inputStream\r\n\r\n  def execute( body: ( InputStream =&gt; Unit ) ): Unit = {\r\n    try {\r\n      body(is)\r\n    }\r\n    finally {\r\n      if ( is != null ) {\r\n        try {\r\n          is.close()\r\n        }\r\n        catch {\r\n          case e:IOException =&gt; System.err.println(e)\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\nobject TryResource {\r\n\r\n  def tryResource[T &lt;: InputStream]( inputStream: T )( body: ( InputStream =&gt; Unit ) ) {\r\n    val construct = new TryResource( inputStream)\r\n    construct.execute( body )\r\n  }\r\n\r\n  def demo1() {\r\n    val construct = new TryResource( new FileInputStream(&quot;pom.xml&quot;) )\r\n    construct.execute( (is: InputStream ) =&gt; {\r\n      val reader = new BufferedReader( new InputStreamReader(is))\r\n      var count = 1\r\n      var line: String = null\r\n      do {\r\n          line = reader.readLine()\r\n          if ( line != null ) {\r\n            printf(&quot;demo1 %5d %s\\n&quot;, count, line)\r\n            count = count + 1\r\n          }\r\n      }\r\n      while ( line != null  )\r\n    } )\r\n    println(&quot;==== Done it ====&quot;)\r\n  }\r\n\r\n  def demo2() {\r\n    tryResource ( new FileInputStream(&quot;pom.xml&quot;) ) ( ( is: InputStream ) =&gt;  {\r\n      val reader = new BufferedReader( new InputStreamReader(is))\r\n      var count = 1\r\n      var line: String = null\r\n      do {\r\n          line = reader.readLine()\r\n          if ( line != null ) {\r\n            printf(&quot;demo2 %5d %s\\n&quot;, count, line)\r\n            count = count + 1\r\n          }\r\n      }\r\n      while ( line != null  )\r\n    } )\r\n    println(&quot;==== Done it ====&quot;)\r\n  }\r\n\r\n  def main( args: Array[String]): Unit = {\r\n    demo2()\r\n  }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>The method <code>tryResource<\/code> takes two parameter lists. The first parameter list is a argument list of one, the input stream resource. The second parameter list is also a argument of one, except it is a so-called <em>called-by-name<\/em> type, by which the function call is delayed, evaluated <em>after<\/em> inside the method. In other words, we call <code>tryResource()()<\/code> by passing a block of code that operates on the <code>InputStream<\/code> and the library function will automatically close the resource for us, regardless of whether block of code completes with normal or abnormal termination. Java 7 now has a similar acquire-release mechanism as a language feature, whereas in Scala, we implemented this a library feature. Scala is scalable language after all.<\/p>\n<p>It would be straight forward now to use <code>tryResource<\/code> in the <code>QuoteRetrieverYahooImpl<\/code> directly. This is an exercise for you, the reader. Hint: You should have a good read about Scala&#8217;s structured types!<\/p>\n<p>Good Luck!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the final part in a series of article about porting an existing Java application to Scala. The application calls the Yahoo! Financial web service and retrieve stock quotes for the user, storing them in a local repository. The original Java project was described in the part one. In the part two of this [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[50,37,44,40,6,105],"tags":[],"_links":{"self":[{"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/posts\/416"}],"collection":[{"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=416"}],"version-history":[{"count":5,"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/posts\/416\/revisions"}],"predecessor-version":[{"id":421,"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/posts\/416\/revisions\/421"}],"wp:attachment":[{"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=416"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=416"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xenonique.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=416"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}