Chapter 7. Working with Integration and Web Services

Because technology stacks evolve continuously, a large area to consider when developing commercial software is the integration between systems. The flexibility and scalability of the Web have seen the proliferation of services built on top of HTTP to integrate systems in a loosely-coupled fashion. Moreover, to be able to navigate through secure networks accessible via firewalls and additional security mechanisms, the HTTP model has been increasingly popular. In this chapter, we are going to cover how to involve Scala when integrating with systems either via Web Services or REST Services exchanging messages in formats such as XML and JSON. In particular, we will consider running such services through the Play Framework.

In this chapter, we will cover the following topics:

  • Generating data bindings from XML schemas as well as SOAP web service classes out of their WSDL description
  • Manipulating XML and JSON in Scala and in particular in the context of the Play Framework
  • Invoking other REST web services from Play, and validating and displaying their response

Binding XML data in Scala

Even if XML has recently stepped down a bit from its ubiquitous position due to the increasing popularity of JSON, both formats will continue to be heavily used to structure data.

In Java, it is a common practice to use the JAXB libraries to create classes that are able to serialize and deserialize XML data and construct XML documents through an API.

In a similar manner, the scalaxb library available for Scala can generate help classes for working with XML and web services. As an example, let's consider a small XML schema, Bookstore.xsd, that defines a set of books as part of a book store as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.books.org"
            xmlns="http://www.books.org"
            elementFormDefault="qualified">
    <xsd:element name="book_store">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="book" type="book_type" 
                             minOccurs="1" maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:complexType name="book_type">
         <xsd:sequence>
             <xsd:element name="title" type="xsd:string"/>
             <xsd:element name="author" type="xsd:string"/>
             <xsd:element name="date" minOccurs="0" type="xsd:string"/>
             <xsd:element name="publisher" type="xsd:string"/>
         </xsd:sequence>
         <xsd:attribute name="ISBN" type="xsd:string"/>
     </xsd:complexType>
</xsd:schema>

A typical book is defined by its title, author, date of publication, and ISBN number, as shown in the following example:

<book ISBN="9781933499185">
  <title>Madame Bovary</title>
  <author>Gustave Flaubert</author>
  <date>1857</date>
  <publisher>Fonolibro</publisher>
</book>

There are several ways one can run scalaxb documented on the www.scalaxb.org website: either directly as a command line tool, through plugins from SBT or Maven, or as a web API hosted on heroku. Since we have essentially used SBT so far and should be comfortable with it, let's use the SBT plugin to create the bindings.

First create a new SBT project entitled wssample by running the following commands in a new terminal window:

> mkdir wssample
> cd wssample
> sbt
> set name:="wssample"
> session save
> 

Now we need to add the scalaxb plugin dependency to a plugins.sbt file under project/ (and at the same time we will add the sbteclipse plugin that enables us to generate an Eclipse project out of the SBT project). The resulting plugins.sbt file will look similar to the following code:

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0")

addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % "1.1.2")
 
resolvers += Resolver.sonatypeRepo("public")

Additionally, we have to slightly modify the build.sbt file to notably include a command that will generate scalaxb XML bindings when compiling with SBT. The resulting build.sbt file will look similar to the following code:

import ScalaxbKeys._

name:="wssample"

scalaVersion:="2.10.2"

scalaxbSettings

libraryDependencies += "net.databinder.dispatch" %% "dispatch-core" % "0.11.0"
 
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M7" % "test"

sourceGenerators in Compile <+= scalaxb in Compile

packageName in scalaxb in Compile := "se.wssample"

Add the Bookstore.xsd schema shown previously to a new src/main/xsd directory created within the project. From now on, each time you invoke the SBT command > compile, scalaxb will generate some Scala classes under the target/scala-2.10/src_managed directory (in the package given in the build.sbt file that is, se.wssample), unless no changes have been made. For instance, in the case of our small example, scalaxb generates the following case classes:

package se.wssample

case class Book_store(book: se.wssample.Book_type*)

case class Book_type(title: String,
  author: String,
  date: Option[String] = None,
  publisher: String,
  ISBN: Option[String] = None)

Note the * at the end of the first case class declaration, which is used to specify varargs (that is, an unspecified number of arguments, so here the Book_store constructor can take several Book_type instances). A possible test class illustrating the usage of the generated code to parse an XML document is given in the BookstoreSpec.scala class as follows:

package se.wssample

import org.scalatest._
import org.scalatest.matchers.Matchers

class BookstoreSpec extends FlatSpec with Matchers {
  "This bookstore" should "contain 3 books" in {
    
    val bookstore =
    <book_store xmlns="http://www.books.org">
        <book ISBN="9781933499185">
            <title>Madame Bovary</title>
            <author>Gustave Flaubert</author>
            <date>1857</date>
            <publisher>Fonolibro</publisher>
        </book>
        <book ISBN="9782070411207">
            <title>Le malade imaginaire</title>
            <author>Moliere</author>
            <date>1673</date>
            <publisher>Gallimard</publisher>
        </book>
        <book ISBN="1475066511">
            <title>Fables</title>
            <author>Jean de La Fontaine</author>
            <date>1678</date>
            <publisher>CreateSpace</publisher>
        </book>
    </book_store>
    
    val bookstoreInstance = scalaxb.fromXML[Book_store](bookstore)
    
    println("bookstoreInstance: "+ bookstoreInstance.book)
    
    bookstoreInstance.book.length should be === 3
  }
}

The expected output from this test when invoking the > sbt test command is as follows:

bookstoreInstance: List(Book_type(Madame Bovary,Gustave Flaubert,Some(1857),Fonolibro,Some(9781933499185)), Book_type(Le malade imaginaire,Molière,Some(1673),Gallimard,Some(9782070411207)), Book_type(Fables,Jean de La Fontaine,Some(1678),CreateSpace,Some(1475066511)))
[info] BookstoreSpec:
[info] This bookstore
[info] - should contain 3 books
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 4 s

Running scalaxb from a SOAP web service

Since scalaxb supports the Web Services Description Language (WSDL), we can also generate a full web service API in addition to the XML data-only related classes. To achieve this functionality, we just need to copy our WSDL service description file to src/main/wsdl. All the files with the .wsdl extension will be processed at compile time by the scalaxb plugin that will create the following three types of output:

  • The service API specific to your application.
  • Classes specific to the SOAP protocol.
  • Classes responsible for sending the SOAP messages to the endpoint URL via HTTP. scalaxb uses the dispatch library that we introduced in Chapter 3, Understanding the Scala Ecosystem. This is why we added it as a dependency into the build.sbt file.

Let's take an online SOAP web service as a way to illustrate the usage of scalaxb from a WSDL description. www.webservicex.net is a site that contains many different samples of such web services in various market segments. Here, we will focus on their Stock Quote service that returns quotes given by a stock symbol. The API is very straightforward since it consists of only one request method, getQuote, and the data it returns is limited in size. You might want to try any other available service (later on as you can have multiple WSDL files in your same project). Its WSDL description looks similar to the following code:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions … // headers > 
  <wsdl:types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://www.webserviceX.NET/">
      <s:element name="GetQuote">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="symbol" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="GetQuoteResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetQuoteResult" type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="string" nillable="true" type="s:string" />
    </s:schema>
  </wsdl:types>
  <wsdl:message name="GetQuoteSoapIn">
    <wsdl:part name="parameters" element="tns:GetQuote" />
  </wsdl:message>
  <wsdl:message name="GetQuoteSoapOut">
    <wsdl:part name="parameters" element="tns:GetQuoteResponse" />
  </wsdl:message>
  ...

The first part of the WSDL file contains the description of the XML schema. The second part defines the various web service operations as follows:

  <wsdl:portType name="StockQuoteSoap">
    <wsdl:operation name="GetQuote">
      <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Get Stock quote for a company Symbol</wsdl:documentation>
      <wsdl:input message="tns:GetQuoteSoapIn" />
      <wsdl:output message="tns:GetQuoteSoapOut" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:portType name="StockQuoteHttpGet">
    <wsdl:operation name="GetQuote">
      <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Get Stock quote for a company Symbol</wsdl:documentation>
      <wsdl:input message="tns:GetQuoteHttpGetIn" />
      <wsdl:output message="tns:GetQuoteHttpGetOut" />
    </wsdl:operation>
  </wsdl:portType>
  
  <wsdl:binding name="StockQuoteSoap12" type="tns:StockQuoteSoap">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="GetQuote">
      <soap12:operation soapAction="http://www.webserviceX.NET/GetQuote" style="document" />
      <wsdl:input>
        <soap12:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap12:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  ...

Finally, the last part of the WSDL file defines the coupling between web service operations and physical URLs:

  <wsdl:service name="StockQuote">
    …
    <wsdl:port name="StockQuoteSoap12" binding="tns:StockQuoteSoap12">
      <soap12:address location="http://www.webservicex.net/stockquote.asmx" />
    </wsdl:port>
    …
  </wsdl:service>
</wsdl:definitions>

As you can see, the WSDL files are often pretty verbose, but the Scala contract resulting from the scalaxb generation boils down to this one method trait:

// Generated by <a href="http://scalaxb.org/">scalaxb</a>.
package se.wssample

trait StockQuoteSoap {
  def getQuote(symbol: Option[String]): Either[scalaxb.Fault[Any], se.wssample.GetQuoteResponse]
}

Notice how the resulting type is nicely wrapped into an Either class that represents a value of one of the two possible types, Left and Right, where the Right object corresponds to a successful invocation of the service whereas the Left object contains a scalaxb.Fault value in case of failure, as we have briefly described in Chapter 2, Code Integration.

Since the generated classes concerning the SOAP protocol and the HTTP dispatch-related classes are not specific to the service we are defining, they can be reused and therefore have been generated as stackable traits including data types and interface, SOAP bindings, and full SOAP clients. A typical usage scenario of these traits to invoke a SOAP web service is given in the following StockQuoteSpec.scala test sample:

package se.wssample

import org.scalatest._
import org.scalatest.matchers.Matchers
import scala.xml.{ XML, PrettyPrinter }

class StockQuoteSpec extends FlatSpec with Matchers {
  "Getting a quote for Apple" should "give appropriate data" in {
    
    val pp = new PrettyPrinter(80, 2)
    
    val service = 
      (new se.wssample.StockQuoteSoap12Bindings 
        with scalaxb.SoapClients 
        with scalaxb.DispatchHttpClients {}).service
    
    val stockquote = service.getQuote(Some("AAPL"))
    
    stockquote match {
      case Left(err) => fail("Problem with stockquote invocation")
      case Right(success) => success.GetQuoteResult match {
        case None => println("No info returned for that quote")
        case Some(x) => {
          println("Stockquote: "+pp.format(XML.loadString(x)))
          x should startWith ("<StockQuotes><Stock><Symbol>AAPL</Symbol>")
        }
      }
    }
  }
}

In this example, once we have instantiated the service, we will just call the API method service.getQuote(Some("AAPL")) for retrieving the stock quote of the AAPL symbol (Apple, Inc). We then pattern match on the result to extract the XML data out of the Either object that was returned by the service. Finally, since the retrieved data is given as a string of XML, we parse it and format it for better reading. We can execute the test using the following code to see what comes out of it:

> sbt
> test-only se.wssample.StockQuoteSpec
Stockquote: <StockQuotes>
  <Stock>
    <Symbol>AAPL</Symbol>
    <Last>553.13</Last>
    <Date>1/2/2014</Date>
    <Time>4:00pm</Time>
    <Change>-7.89</Change>
    <Open>555.68</Open>
    <High>557.03</High>
    <Low>552.021</Low>
    <Volume>8388321</Volume>
    <MktCap>497.7B</MktCap>
    <PreviousClose>561.02</PreviousClose>
    <PercentageChange>-1.41%</PercentageChange>
    <AnnRange>385.10 - 575.14</AnnRange>
    <Earns>39.75</Earns>
    <P-E>14.11</P-E>
    <Name>Apple Inc.</Name>
  </Stock>
</StockQuotes>
[info] StockQuoteSpec:
[info] Getting a quote for Apple
[info] - should give appropriate data
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 3 s
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.250.11