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
- Download a wsdl document locally.
- 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
- 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 (useDispatchHttpClientsAsync
)
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.