wsdl 1.1 support

During the .com boom, everyone has written a SOAP service or two, which were typically cataloged using WSDL. If you study a WSDL document, you'll discover that a good portion of the document is actually embedded XML Schema document describing the layout of the messages sent back and forth. The rest are details. Since scalaxb is handling the XML Schema, it was only matter of time to extend this into supporting WSDL.

usage

  1. Download a wsdl document locally.
  2. If you're using sbt-scalaxb place it under src/main/wsdl.

Here's the sample setup:

import ScalaxbKeys._
 
val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "1.0.2"
val scalaParser = "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1"
val dispatchV = "0.11.1" // change this to appropriate dispatch version
val dispatch = "net.databinder.dispatch" %% "dispatch-core" % dispatchV
 
organization := "com.example"
 
name := "scalaxb-stockquote-sample"
 
scalaVersion := "2.11.1"
 
scalaxbSettings
 
packageName in (Compile, scalaxb) := "stockquote"
 
dispatchVersion in (Compile, scalaxb) := dispatchV
 
async in (Compile, scalaxb) := true
 
sourceGenerators in Compile <+= scalaxb in Compile
 
libraryDependencies ++= Seq(scalaXml, scalaParser, dispatch)

This should generate the following 9 files:

  • scalaxb/httpclients_async.scala
  • scalaxb/httpclients_dispatch_async.scala
  • scalaxb/scalaxb.scala
  • scalaxb/soap12_async.scala
  • soapenvelope12/soapenvelope12.scala
  • soapenvelope12/soapenvelope12_xmlprotocol.scala
  • stockquote/stockquote.scala
  • stockquote/stockquote_type1.scala
  • stockquote/xmlprotocol.scala
  1. Use the code as follows:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
 
val service = (new stockquote.StockQuoteSoap12Bindings with
  scalaxb.SoapClientsAsync with
  scalaxb.DispatchHttpClientsAsync {}).service
val fresponse = service.getQuote(Some("GOOG"))
val response = Await.result(fresponse, 5 seconds)
println(response)

what are they?

Before I get into all that, I must tell you that I will be using Cake pattern, so if you're not sure what it is, read it up.

So let's look at the generated files. First stockquote.scala:

// Generated by <a href="http://scalaxb.org/">scalaxb</a>.
package stockquote
 
import scala.concurrent.Future
 
trait StockQuoteSoap {
  def getQuote(symbol: Option[String]): Future[stockquote.GetQuoteResponse]
}

So this defines the interface portion of the web surface, which is abstracted away from XML or SOAP. Next.

I am not going to quote the entire stockquote_xmlprotocol.scala, but it implements the parsing of XML into case classes, and with wsdl it also implements the SOAP 1.2 binding of the interface as follows:

 
  trait StockQuoteSoap12Bindings { this: scalaxb.SoapClientsAsync =>
    lazy val targetNamespace: Option[String] = Some("http://www.webserviceX.NET/")
    lazy val service: stockquote.StockQuoteSoap = new StockQuoteSoap12Binding {}
    def baseAddress = new java.net.URI("http://www.webservicex.net/stockquote.asmx")
 
    trait StockQuoteSoap12Binding extends stockquote.StockQuoteSoap {
      import scalaxb.ElemName._
      def getQuote(symbol: Option[String]): Future[stockquote.GetQuoteResponse] = 
        soapClient.requestResponse(scalaxb.toXML(stockquote.GetQuote(symbol), Some("http://www.webserviceX.NET/"), "GetQuote", defaultScope),
            Nil, defaultScope, baseAddress, "POST", Some(new java.net.URI("http://www.webserviceX.NET/GetQuote"))).transform({ case (header, body) => 
            scalaxb.fromXML[stockquote.GetQuoteResponse]((body.headOption getOrElse {body}), Nil) }, {
              case x: scalaxb.Fault[_] => x
              case x => x
            })
    }
  }

So in Cake pattern, StockQuoteSoap12Bindings represents a module like a slice of cake. It's declaring that it depends on another module called scalaxb.SoapClientsAsync, which is declared in soap12_async.scala.

trait SoapClientsAsync { this: HttpClientsAsync =>
  lazy val soapClient: SoapClientAsync = new SoapClientAsync {}
  def baseAddress: java.net.URI
 
  trait SoapClientAsync {
    implicit lazy val executionContext = scala.concurrent.ExecutionContext.Implicits.global
    import soapenvelope12.{Fault => _, _}
    val SOAP_ENVELOPE_URI = "http://www.w3.org/2003/05/soap-envelope"
 
    def requestResponse(body: scala.xml.NodeSeq, headers: scala.xml.NodeSeq, scope: scala.xml.NamespaceBinding,
                        address: java.net.URI, webMethod: String, action: Option[java.net.URI]):
        Future[(scala.xml.NodeSeq, scala.xml.NodeSeq)] = {
      val bodyRecords = body.toSeq map {DataRecord(None, None, _)}
      val headerOption = headers.toSeq.headOption map { _ =>
        Header(headers.toSeq map {DataRecord(None, None, _)}, Map())
      }
      val envelope = Envelope(headerOption, Body(bodyRecords, Map()), Map())
      buildResponse(soapRequest(Some(envelope), scope, address, webMethod, action))
    }
    ....
  }
}

This is scalaxb's async implementation of SOAP, which internally uses scalaxb-generated SOAP envelope case classes defined in soapenvelope12.scala and xmlprotocol.scala. But more importantly, scalaxb.SoapClientsAsync module depends on scalaxb.HttpClientsAsync module, which is currently defined as follows:

package scalaxb
 
import concurrent.Future
 
trait HttpClientsAsync {
  def httpClient: HttpClient
 
  trait HttpClient {
    def request(in: String, address: java.net.URI, headers: Map[String, String]): Future[String]
  }
}

This is an abstract trait because the implementation of httpClient is not provided, which brings us to the final file httpclients_dispatch_async.scala.

DispatchHttpClientsAsync

scalaxb.DispatchHttpClientsAsync is a reference implementation of scalaxb.HttpClientsAsync module.

package scalaxb
 
import concurrent.Future
 
trait DispatchHttpClientsAsync extends HttpClientsAsync {
  lazy val httpClient = new DispatchHttpClient {}
 
  trait DispatchHttpClient extends HttpClient {
    import dispatch._, Defaults._
 
    val http = new Http()
    def request(in: String, address: java.net.URI, headers: Map[String, String]): concurrent.Future[String] = {
      val req = url(address.toString).setBodyEncoding("UTF-8") <:< headers << in
      http(req > as.String)
    }
  }
}

scalaxb supports Dispatch 0.8, 0.10.x, and 0.11.x. Because the generated code is slightly different for Dispatch versions, dispatchVersion in (Compile, scalaxb) must be set. Current default is 0.11.1, but this will likely change in the future.

val dispatchV = "0.11.1" // change this to appropriate dispatch version
val dispatch = "net.databinder.dispatch" %% "dispatch-core" % dispatchV
 
dispatchVersion in (Compile, scalaxb) := dispatchV
 
async in (Compile, scalaxb) := true // set to false for older version of dispatch
 
libraryDependencies ++= Seq(dispatch, ....)

pieces of cakes

So here's the deal.

  • generated stockquote.StockQuoteSoap12Bindings module depends on ...
  • scalaxb.SoapClientsAsync module depends on ...
  • an implementation of scalaxb.HttpClientsAsync module (use DispatchHttpClientsAsync)

Why did I do this? The answer is modularity, meaning I can upgrade my car stereo without worry about the braking system breaking. For whatever reason you need your own http handing? Write your own HttpClientsAsync module. You want to send password in the SOAP header? Extend scalaxb.SoapClientsAsync.

let me know

If you have questions and feedback, please drop a line to the mailing list or tweet to @scalaxb. I might look into it.