wsdl 1.1 support
Even if you ask a room full of programmers if any of them have written an XML Schema document, I am guessing few of them would raise their hands. SOAP on the other hand, would be a different story. During the .com boom, everyone has written a SOAP service or two, which are 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.
-
Run scalaxb as follows:
$ scalaxb -p stockquote stockquote.wsdl
This should generate the following 7 files:
- stockquote.scala
- xmlprotocol.scala
- scalaxb.scala
- soap.scala
- httpclients_dispatch.scala
- soapenvelope12.scala
- soapenvelope12_xmlprotocol.scala
- Use the code as follows:
val service = (new stockquote.StockQuoteSoap12Bindings with scalaxb.SoapClients with scalaxb.DispatchHttpClients {}).service println(service.getQuote(Some("GOOG")))
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 case class GetQuote(symbol: Option[String]) case class GetQuoteResponse(GetQuoteResult: Option[String]) trait StockQuoteSoap { def getQuote(symbol: Option[String]): Either[scalaxb.Fault[Any], Option[String]] }
So this defines the interface portion of the web surface, which is abstracted away from XML or SOAP, except for some error handling Either. 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.SoapClients => lazy val targetNamespace: Option[String] = Some("http://www.webserviceX.NET/") lazy val service: StockQuoteSoap = new StockQuoteSoap12 {} lazy val baseAddress = new java.net.URI("http://www.webservicex.net/stockquote.asmx") trait StockQuoteSoap12Binding extends StockQuoteSoap { def getQuote(symbol: Option[String]): Either[scalaxb.Fault[Any], Option[String]] = soapClient.requestResponse(scalaxb.toXML(GetQuote(symbol), targetNamespace, "GetQuote", defaultScope), defaultScope, baseAddress, "POST", Some(new java.net.URI("http://www.webserviceX.NET/GetQuote"))) match { case Left(x) => Left(x) case Right(x) => Right(scalaxb.fromXML[GetQuoteResponse](x).GetQuoteResult) } } }
So in Cake pattern, StockQuoteSoap12Bindings represents a module like a slice of cake. It's declaring that it depends on another module called scalaxb.SoapClients, which is declared in soap.scala.
trait SoapClients { this: HttpClients => import soapenvelope12.{Envelope, Body, Detail} lazy val soapClient: SoapClient = new SoapClient {} val baseAddress: java.net.URI trait SoapClient { val SOAP_ENVELOPE_URI = "http://www.w3.org/2003/05/soap-envelope" def requestResponse(in: scala.xml.NodeSeq, scope: scala.xml.NamespaceBinding, address: java.net.URI, webMethod: String, action: Option[java.net.URI]): Either[Fault[Detail], scala.xml.Node] = { val envelope = Envelope(None, Body(Seq(DataRecord(None, None, in)), Map()), Map()) buildResponse(soapRequest(Some(envelope), scope, address, webMethod, action)) } ... } }
This is my implementation of SOAP, which internally uses scalaxb-generated SOAP envelope case classes defined in soapenvelope12.scala and xmlprotocol.scala. But more importantly, scalaxb.SoapClients module depends on scalaxb.HttpClients module, which is currently defined as follows:
trait HttpClients { val httpClient: HttpClient trait HttpClient { def request(in: String, address: java.net.URI, action: Option[java.net.URI]): String } }
This is an abstract trait because the implementation of httpClient is not provided, which brings us to the final file httpclients_dispatch.scala:
trait DispatchHttpClients extends HttpClients { val httpClient = new DispatchHttpClient {} trait DispatchHttpClient extends HttpClient { import dispatch._ def request(in: String, address: java.net.URI, action: Option[java.net.URI]): String = { val http = new Http val header = Map(action.toList map { x => ("SOAPAction", "\"%s\"".format(x.toString)) }: _*) http(url(address.toString) << (in, "application/soap+xml") <:< header as_str) } } }
This is a reference implementation of scalaxb.HttpClients module.
pieces of cakes
So here's the deal.
- generated
stockquote.StockQuoteSoap12Bindingsmodule depends on ... scalaxb.SoapClientsmodule depends on ...- an implementation of
scalaxb.HttpClientsmodule (useDispatchHttpClients)
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 HttpClients module. You want to send password in the SOAP header? Extend scalaxb.SoapClients.
let me know
This is an experimental support at this point. If you have questions and feedback, please drop a line to the mailing list or tweet to @scalaxb.
- Login to post comments
