problem handling rpc style return message #94

Closed
dkhenry opened this Issue Oct 19, 2011 · 7 comments

Comments

Projects
None yet
2 participants
@dkhenry

dkhenry commented Oct 19, 2011

steps

  1. compile rpc style soap 1.1 wsdl with responses. (such as SevOne API)
  2. use it against some service.

problem

scalaxb.ParserFailure: seq must be scala.xml.Node

expectation

response is handled correctly.

note

in the following generated code, x.head \ "return" returns NodeSeq (because there may be many elements named "return").

def core_getPluginByTextId(textId: String): Either[scalaxb.Fault[Any], SO_Plugin] = 
  soapClient.requestResponse(scala.xml.Elem(targetNamespace map {defaultScope.getPrefix(_)} getOrElse {""}, "core_getPluginByTextId", scala.xml.Null, defaultScope,
    scalaxb.toXML(textId, Some("http://www.sevone.com/"), "textId", defaultScope): _*), defaultScope, baseAddress, "POST", Some(new java.net.URI("urn:SevOneApi#SevOneApiServer#core_getPluginByTextId"))) match {
    case Left(x)  => Left(x)
    case Right(x) => Right(scalaxb.fromXML[SO_Plugin](x.head \ "return"))
  }

original

about line 635 of scalaxb.scala we have this.

I am pretty confident it will always take the default case and throw an error. I am guessing we need to iterate over the NodeSeq and return a List[A] ?

trait ElemNameParser[A] extends AnyElemNameParser with XMLFormat[A] with CanWriteChildNodes[A] {
  def reads(seq: scala.xml.NodeSeq, stack: List[ElemName]): Either[String, A] = seq match {
    case node: scala.xml.Node =>
      parse(parser(node, stack), node.child) match {
        case x: Success[_] => Right(x.get)
        case x: Failure => Left(parserErrorMsg(x.msg, x.next, ElemName(node) :: stack))
        case x: Error => Left(parserErrorMsg(x.msg, node))
      }
    case _ => Left("seq must be scala.xml.Node")
  }
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Oct 19, 2011

Owner

scala.xml.Node extends scala.xml.NodeSeq. Could you provide a reproduction step that would actually cause problem? NodeSeq is passed in here for symmetry (some object outputs more than one Node iirc).

Owner

eed3si9n commented Oct 19, 2011

scala.xml.Node extends scala.xml.NodeSeq. Could you provide a reproduction step that would actually cause problem? NodeSeq is passed in here for symmetry (some object outputs more than one Node iirc).

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Oct 19, 2011

Owner
scala> val foo: scala.xml.NodeSeq = <foo/>
foo: scala.xml.NodeSeq = <foo></foo>

scala> foo  match {
     | case node: scala.xml.Node => node.toString
     | case _ => sys.error("boom")
     | }
res0: String = <foo></foo>
Owner

eed3si9n commented Oct 19, 2011

scala> val foo: scala.xml.NodeSeq = <foo/>
foo: scala.xml.NodeSeq = <foo></foo>

scala> foo  match {
     | case node: scala.xml.Node => node.toString
     | case _ => sys.error("boom")
     | }
res0: String = <foo></foo>

@eed3si9n eed3si9n closed this Oct 19, 2011

@dkhenry

This comment has been minimized.

Show comment
Hide comment
@dkhenry

dkhenry Oct 19, 2011

I am getting this xml back

    <ns1:core_getEnabledPluginsByDeviceIdResponse xmlns:xsi="
    http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="
    http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="
    http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://www.sevone.com/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><return
    xsi:type="ns1:ArrayOfstring" SOAP-ENC:arrayType="xsd:string[15]"><item
    xsi:type="xsd:string">BULKDATA</item><item
    xsi:type="xsd:string">CALLMANAGER</item><item
    xsi:type="xsd:string">DEFERRED</item><item
    xsi:type="xsd:string">DNS</item><item xsi:type="xsd:string">HTTP</item><item
    xsi:type="xsd:string">ICMP</item><item
    xsi:type="xsd:string">IPSLA</item><item
    xsi:type="xsd:string">MYSQLDB</item><item
    xsi:type="xsd:string">NBAR</item><item
    xsi:type="xsd:string">PORTSHAKER</item><item
    xsi:type="xsd:string">PROCESS</item><item
    xsi:type="xsd:string">PROXYPING</item><item
    xsi:type="xsd:string">SNMP</item><item
    xsi:type="xsd:string">WEBSTATUS</item><item
    xsi:type="xsd:string">WMI</item></return></ns1:core_getEnabledPluginsByDeviceIdResponse>

and its causing it to throw the following error

Error:scalaxb.ParserFailure: seq must be scala.xml.Node
scalaxb.package$.fromXML(scalaxb.scala:13)
com.SevOne.XMLProtocol$SevOneApiBindings$SevOneApiBinding$class.core_getEnabledPluginsByDeviceId(xmlprotocol.scala:12810)
com.SevOne.XMLProtocol$SevOneApiBindings$$anon$347.core_getEnabledPluginsByDeviceId(xmlprotocol.scala:12568)
com.SevOne.Main$.main(Main.scala:57)
com.SevOne.Main.main(Main.scala)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:616)
sbt.Run.invokeMain(Run.scala:60)
sbt.Run.run0(Run.scala:53)
sbt.Run.execute$1(Run.scala:42)
sbt.Run$$anonfun$run$1.apply$mcV$sp(Run.scala:46)
sbt.TrapExit$.executeMain$1(TrapExit.scala:33)
sbt.TrapExit$$anon$1.run(TrapExit.scala:42)

I made the error disappear by changing scalaxb:635 from seq to seq.last that
actually gives me somewhat valid output that looks like this

Right(ArrayOfstring(Some(ArraySequence(List(DataRecord(item,BULKDATA),
DataRecord(item,CALLMANAGER), DataRecord(item,DEFERRED),
DataRecord(item,DNS), DataRecord(item,HTTP), DataRecord(item,ICMP),
DataRecord(item,IPSLA), DataRecord(item,MYSQLDB), DataRecord(item,NBAR),
DataRecord(item,PORTSHAKER), DataRecord(item,PROCESS),
DataRecord(item,PROXYPING), DataRecord(item,SNMP),
DataRecord(item,WEBSTATUS),
DataRecord(item,WMI)))),Some(xsd:string[15]),None,None,None,Map(@{
http://www.w3.org/2001/XMLSchema-instance}type -> DataRecord({
http://www.w3.org/2001/XMLSchema-instance}type,ns1:ArrayOfstring))))

dkhenry commented Oct 19, 2011

I am getting this xml back

    <ns1:core_getEnabledPluginsByDeviceIdResponse xmlns:xsi="
    http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="
    http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="
    http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://www.sevone.com/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><return
    xsi:type="ns1:ArrayOfstring" SOAP-ENC:arrayType="xsd:string[15]"><item
    xsi:type="xsd:string">BULKDATA</item><item
    xsi:type="xsd:string">CALLMANAGER</item><item
    xsi:type="xsd:string">DEFERRED</item><item
    xsi:type="xsd:string">DNS</item><item xsi:type="xsd:string">HTTP</item><item
    xsi:type="xsd:string">ICMP</item><item
    xsi:type="xsd:string">IPSLA</item><item
    xsi:type="xsd:string">MYSQLDB</item><item
    xsi:type="xsd:string">NBAR</item><item
    xsi:type="xsd:string">PORTSHAKER</item><item
    xsi:type="xsd:string">PROCESS</item><item
    xsi:type="xsd:string">PROXYPING</item><item
    xsi:type="xsd:string">SNMP</item><item
    xsi:type="xsd:string">WEBSTATUS</item><item
    xsi:type="xsd:string">WMI</item></return></ns1:core_getEnabledPluginsByDeviceIdResponse>

and its causing it to throw the following error

Error:scalaxb.ParserFailure: seq must be scala.xml.Node
scalaxb.package$.fromXML(scalaxb.scala:13)
com.SevOne.XMLProtocol$SevOneApiBindings$SevOneApiBinding$class.core_getEnabledPluginsByDeviceId(xmlprotocol.scala:12810)
com.SevOne.XMLProtocol$SevOneApiBindings$$anon$347.core_getEnabledPluginsByDeviceId(xmlprotocol.scala:12568)
com.SevOne.Main$.main(Main.scala:57)
com.SevOne.Main.main(Main.scala)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:616)
sbt.Run.invokeMain(Run.scala:60)
sbt.Run.run0(Run.scala:53)
sbt.Run.execute$1(Run.scala:42)
sbt.Run$$anonfun$run$1.apply$mcV$sp(Run.scala:46)
sbt.TrapExit$.executeMain$1(TrapExit.scala:33)
sbt.TrapExit$$anon$1.run(TrapExit.scala:42)

I made the error disappear by changing scalaxb:635 from seq to seq.last that
actually gives me somewhat valid output that looks like this

Right(ArrayOfstring(Some(ArraySequence(List(DataRecord(item,BULKDATA),
DataRecord(item,CALLMANAGER), DataRecord(item,DEFERRED),
DataRecord(item,DNS), DataRecord(item,HTTP), DataRecord(item,ICMP),
DataRecord(item,IPSLA), DataRecord(item,MYSQLDB), DataRecord(item,NBAR),
DataRecord(item,PORTSHAKER), DataRecord(item,PROCESS),
DataRecord(item,PROXYPING), DataRecord(item,SNMP),
DataRecord(item,WEBSTATUS),
DataRecord(item,WMI)))),Some(xsd:string[15]),None,None,None,Map(@{
http://www.w3.org/2001/XMLSchema-instance}type -> DataRecord({
http://www.w3.org/2001/XMLSchema-instance}type,ns1:ArrayOfstring))))
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Oct 19, 2011

Owner

Is the code generated using https://gist.github.com/1229349?

Owner

eed3si9n commented Oct 19, 2011

Is the code generated using https://gist.github.com/1229349?

@eed3si9n eed3si9n reopened this Oct 19, 2011

@dkhenry

This comment has been minimized.

Show comment
Hide comment
@dkhenry

dkhenry Oct 19, 2011

Its generated using the full wsdl which I can't put in a gist ( its too
large ).

On Wed, Oct 19, 2011 at 4:43 PM, eugene yokota <
reply@reply.github.com>wrote:

Is the code generated using https://gist.github.com/1229349?

Reply to this email directly or view it on GitHub:
#94 (comment)

S.D.G.

dkhenry commented Oct 19, 2011

Its generated using the full wsdl which I can't put in a gist ( its too
large ).

On Wed, Oct 19, 2011 at 4:43 PM, eugene yokota <
reply@reply.github.com>wrote:

Is the code generated using https://gist.github.com/1229349?

Reply to this email directly or view it on GitHub:
#94 (comment)

S.D.G.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Oct 19, 2011

Owner

Could you email it to me? (eed3si9n at gmail)

Owner

eed3si9n commented Oct 19, 2011

Could you email it to me? (eed3si9n at gmail)

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Oct 21, 2011

Owner

Here's the code that reproduces the issue caused by core_getPluginByTextId.

/*
      def core_getPollsByDeviceId(deviceId: BigInt): Either[scalaxb.Fault[Any], ArrayOfSO_Poll] = 
        soapClient.requestResponse(scala.xml.Elem(targetNamespace map {defaultScope.getPrefix(_)} getOrElse {""}, "core_getPollsByDeviceId", scala.xml.Null, defaultScope,
          scalaxb.toXML(deviceId, Some("http://www.sevone.com/"), "deviceId", defaultScope): _*), defaultScope, baseAddress, "POST", Some(new java.net.URI("urn:SevOneApi#SevOneApiServer#core_getPollsByDeviceId"))) match {
          case Left(x)  => Left(x)
          case Right(x) => Right(scalaxb.fromXML[ArrayOfSO_Poll](x.head \ "return"))
        }
*/  
object Main extends App {
  val x = <ns1:core_getEnabledPluginsByDeviceIdResponse
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:ns1="http://www.sevone.com/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><return
          xsi:type="ns1:ArrayOfstring" SOAP-ENC:arrayType="xsd:string[15]"><item
          xsi:type="xsd:string">BULKDATA</item><item
          xsi:type="xsd:string">CALLMANAGER</item><item
          xsi:type="xsd:string">DEFERRED</item><item
          xsi:type="xsd:string">DNS</item><item xsi:type="xsd:string">HTTP</item><item
          xsi:type="xsd:string">ICMP</item><item
          xsi:type="xsd:string">IPSLA</item><item
          xsi:type="xsd:string">MYSQLDB</item><item
          xsi:type="xsd:string">NBAR</item><item
          xsi:type="xsd:string">PORTSHAKER</item><item
          xsi:type="xsd:string">PROCESS</item><item
          xsi:type="xsd:string">PROXYPING</item><item
          xsi:type="xsd:string">SNMP</item><item
          xsi:type="xsd:string">WEBSTATUS</item><item
          xsi:type="xsd:string">WMI</item></return></ns1:core_getEnabledPluginsByDeviceIdResponse>  

   val plugin = Right(scalaxb.fromXML[sevone.SO_Plugin](x.head \ "return"))
   println(plugin)
}

Looking at the code, however, it looks like the problem is with the wsdl-generated code, not scalaxb.scala.
It is correct for fromXML to reject anything but scala.xml.Node. The problem is x.head \ "return" returns NodeSeq.
This should be (x.head \ "return").head instead.

Owner

eed3si9n commented Oct 21, 2011

Here's the code that reproduces the issue caused by core_getPluginByTextId.

/*
      def core_getPollsByDeviceId(deviceId: BigInt): Either[scalaxb.Fault[Any], ArrayOfSO_Poll] = 
        soapClient.requestResponse(scala.xml.Elem(targetNamespace map {defaultScope.getPrefix(_)} getOrElse {""}, "core_getPollsByDeviceId", scala.xml.Null, defaultScope,
          scalaxb.toXML(deviceId, Some("http://www.sevone.com/"), "deviceId", defaultScope): _*), defaultScope, baseAddress, "POST", Some(new java.net.URI("urn:SevOneApi#SevOneApiServer#core_getPollsByDeviceId"))) match {
          case Left(x)  => Left(x)
          case Right(x) => Right(scalaxb.fromXML[ArrayOfSO_Poll](x.head \ "return"))
        }
*/  
object Main extends App {
  val x = <ns1:core_getEnabledPluginsByDeviceIdResponse
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:ns1="http://www.sevone.com/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><return
          xsi:type="ns1:ArrayOfstring" SOAP-ENC:arrayType="xsd:string[15]"><item
          xsi:type="xsd:string">BULKDATA</item><item
          xsi:type="xsd:string">CALLMANAGER</item><item
          xsi:type="xsd:string">DEFERRED</item><item
          xsi:type="xsd:string">DNS</item><item xsi:type="xsd:string">HTTP</item><item
          xsi:type="xsd:string">ICMP</item><item
          xsi:type="xsd:string">IPSLA</item><item
          xsi:type="xsd:string">MYSQLDB</item><item
          xsi:type="xsd:string">NBAR</item><item
          xsi:type="xsd:string">PORTSHAKER</item><item
          xsi:type="xsd:string">PROCESS</item><item
          xsi:type="xsd:string">PROXYPING</item><item
          xsi:type="xsd:string">SNMP</item><item
          xsi:type="xsd:string">WEBSTATUS</item><item
          xsi:type="xsd:string">WMI</item></return></ns1:core_getEnabledPluginsByDeviceIdResponse>  

   val plugin = Right(scalaxb.fromXML[sevone.SO_Plugin](x.head \ "return"))
   println(plugin)
}

Looking at the code, however, it looks like the problem is with the wsdl-generated code, not scalaxb.scala.
It is correct for fromXML to reject anything but scala.xml.Node. The problem is x.head \ "return" returns NodeSeq.
This should be (x.head \ "return").head instead.

@eed3si9n eed3si9n closed this in e4959d7 Oct 21, 2011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment