[howto] pass a byte array to a web service

Hello all,

I have a web service developed in java with axis and I'd like to invoke
it by means of SOAP4R. The problem is that I don't know how to pass an
array of bytes, without starting from the WSDL. I tried with
String#pack, an array with numbers or chars (ie: [121] or ['a'] or
[65.chr]), but they all fail.

On customer side I always get:
SOAP::FaultError: java.lang.IllegalArgumentException: argument type
mismatch
        from #<SOAP::Mapping::Object:0x2bc5468>

Please consider that the argument is defined in wsdl as:
<wsdl:part name="x" type="xsd:base64Binary"/>
("x" is obviously a fake name)

I tried simply with String as well, but I get:
SOAP::FaultError: org.xml.sax.SAXException: Bad types (class
java.lang.String ->
class [B)
        from #<SOAP::Mapping::Object:0x2cec320>

We tried calling the web service in the following way:
require 'soap/rpc/driver'
s =
SOAP::RPC::Driver.new('http://localhost:8080/fake/services/BigFake',
'http://www.fake.com/fake')
s.add_method_with_soapaction("fakeMethod",
'http://localhost:8080/fake/services/BigFake/fakeMethod', ['in', 'x'])
x = ['a']
p s.fakeMethod(x)

Of course in reality we are calling a more complex method, with 8 input
parameters, 1 output parameter and a return value, but we have problem
only with the byte[] one.

The equivalent java version (the one that works) is a lot more complex,
hence it is not useful to post it here, but consider that the parameter
is passed to an axis call client as a native byte[].

We have tried to invoke the web service starting from the WSDL, and
surprisingly it works passing a String, which is not so understandable
to me:
require 'soap/wsdlDriver'
WSDL_URL = "http://localhost:8080/fake/services/BigFake?wsdl"
s = SOAP::WSDLDriverFactory.new(WSDL_URL).createDriver
x = "abc"
p s.fakeMethod(x)

Why the latter works, but the former, even passing a String, doesn't
work? Can anybody shed some light on this?

TIA

<aastanti@hotmail.com> schrieb im Newsbeitrag
news:1112713106.994329.50970@f14g2000cwb.googlegroups.com...

Hello all,

I have a web service developed in java with axis and I'd like to invoke
it by means of SOAP4R. The problem is that I don't know how to pass an
array of bytes, without starting from the WSDL. I tried with
String#pack, an array with numbers or chars (ie: [121] or ['a'] or
[65.chr]), but they all fail.

On customer side I always get:
SOAP::FaultError: java.lang.IllegalArgumentException: argument type
mismatch
        from #<SOAP::Mapping::Object:0x2bc5468>

Please consider that the argument is defined in wsdl as:
<wsdl:part name="x" type="xsd:base64Binary"/>
("x" is obviously a fake name)

Withoug knowing web services inside out: The line above seems to indicate
that you have to send the bytes base64 encoded.

require 'base64'

=> true

Base64.encode64 "\001\002\003"

=> "AQID\n"

Base64.encode64 "\001"

=> "AQ==\n"

Kind regards

    robert

···

I tried simply with String as well, but I get:
SOAP::FaultError: org.xml.sax.SAXException: Bad types (class
java.lang.String ->
class [B)
        from #<SOAP::Mapping::Object:0x2cec320>

We tried calling the web service in the following way:
require 'soap/rpc/driver'
s =
SOAP::RPC::Driver.new('http://localhost:8080/fake/services/BigFake&#39;,
'Fake News - Fake Landscapes)
s.add_method_with_soapaction("fakeMethod",
'http://localhost:8080/fake/services/BigFake/fakeMethod&#39;, ['in', 'x'])
x = ['a']
p s.fakeMethod(x)

Of course in reality we are calling a more complex method, with 8 input
parameters, 1 output parameter and a return value, but we have problem
only with the byte one.

The equivalent java version (the one that works) is a lot more complex,
hence it is not useful to post it here, but consider that the parameter
is passed to an axis call client as a native byte.

We have tried to invoke the web service starting from the WSDL, and
surprisingly it works passing a String, which is not so understandable
to me:
require 'soap/wsdlDriver'
WSDL_URL = "http://localhost:8080/fake/services/BigFake?wsdl&quot;
s = SOAP::WSDLDriverFactory.new(WSDL_URL).createDriver
x = "abc"
p s.fakeMethod(x)

Why the latter works, but the former, even passing a String, doesn't
work? Can anybody shed some light on this?

TIA

aastanti@hotmail.com wrote:

Hello all,

I have a web service developed in java with axis and I'd like to invoke
it by means of SOAP4R. The problem is that I don't know how to pass an
array of bytes, without starting from the WSDL. I tried with
String#pack, an array with numbers or chars (ie: [121] or ['a'] or
[65.chr]), but they all fail.

On customer side I always get:
SOAP::FaultError: java.lang.IllegalArgumentException: argument type
mismatch
        from #<SOAP::Mapping::Object:0x2bc5468>

Please consider that the argument is defined in wsdl as:
<wsdl:part name="x" type="xsd:base64Binary"/>
("x" is obviously a fake name)

I tried simply with String as well, but I get:
SOAP::FaultError: org.xml.sax.SAXException: Bad types (class
java.lang.String ->
class [B)
        from #<SOAP::Mapping::Object:0x2cec320>

We tried calling the web service in the following way:
require 'soap/rpc/driver'
s =
SOAP::RPC::Driver.new('http://localhost:8080/fake/services/BigFake&#39;,
'Fake News - Fake Landscapes)
s.add_method_with_soapaction("fakeMethod",
'http://localhost:8080/fake/services/BigFake/fakeMethod&#39;, ['in', 'x'])
x = ['a']
p s.fakeMethod(x)

Of course in reality we are calling a more complex method, with 8 input
parameters, 1 output parameter and a return value, but we have problem
only with the byte one.

The equivalent java version (the one that works) is a lot more complex,
hence it is not useful to post it here, but consider that the parameter
is passed to an axis call client as a native byte.

We have tried to invoke the web service starting from the WSDL, and
surprisingly it works passing a String, which is not so understandable
to me:
require 'soap/wsdlDriver'
WSDL_URL = "http://localhost:8080/fake/services/BigFake?wsdl&quot;
s = SOAP::WSDLDriverFactory.new(WSDL_URL).createDriver
x = "abc"
p s.fakeMethod(x)

Why the latter works, but the former, even passing a String, doesn't
work? Can anybody shed some light on this?

TIA

Check out this URL

http://books.xmlschemata.org/relaxng/ch17-77011.html

In particular check out this

Restrictions

RFC 2045 is defined as transfering binary contents over text-based mail systems. It imposes a line break at least every 76 characters to avoid the inclusion of arbitrary line breaks by the mail systems. Sending base64 content without line breaks is nevertheless a common usage for applications such as SOAP and the W3C XML Schema Working Group. After a request from other W3C Working Groups, the W3C XML Schema Working Group decided to remove the obligation to include these line breaks from the constraints on the lexical space. (This decision was made after the publication of the W3C XML Schema Recommendation. It is now noted in the errata.)

When you use the base64 library, line breaks are automatically inserted.

irb(main):003:0> Base64.encode64("abc")
=> "YWJj\n"

Try removing the line breaks and see if that helps.
irb(main):004:0> Base64.encode64("abc").gsub!(/\n/,"")
=> "YWJj"

Ernie

Robert Klemme wrote:

Withoug knowing web services inside out: The line above seems to

indicate

that you have to send the bytes base64 encoded.

>> require 'base64'
=> true
>> Base64.encode64 "\001\002\003"
=> "AQID\n"
>> Base64.encode64 "\001"
=> "AQ==\n"

Thanks Robert, but it doesn't work: it doesn't seem to be a matter of
encoding, it seems to be a matter of type conversion. Infact, if a try
to encode it like x = Base64.encode("abc") I get again:
Bad types (class java.lang.String -> class [B)

Please also note that starting from the WSDL it works perfectly passing
a plain simple string. I really don't understand.

AA

Unfortunately that doesn't work either, I always get the same error as
expected:
SOAP::FaultError: org.xml.sax.SAXException: Bad types (class
java.lang.String -> class [B)
        from #<SOAP::Mapping::Object:0x2c3e848>

I'll try to speculate. The point seems to be that working with RPC
Driver makes certain assumptions about types: while this is somewhat
irrelevant in ruby world, it becomes relevant when working with web
services in general and java in particular. The SAX parser argues about
the type of that parameter: it expects an array of B (I have to check
what B is), while I'm passing a ruby string that is recognized as a
java string after unmarshaling.

Evidently working with WSDLDriverFactory I get an exact stub, with
precise types for every parameters
I'll try with Lyndon Samson suggestion, it seems worth spending some
more time.
Thanks
AA

Run a HTTP monitor in between the client and server.
Compare and contrast the working and failing code.

All java Strings are 16 bit Unicode, I'm pretty sure ruby uses 8 bit ascii.

good luck!

···

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

Ok, I have used an HTTP monitor to check what's going on. The result is
that working with SOAP::RPC::Driver and add_method_with_soapaction a
type is automatically attached to every parameter. Infact, in the SOAP
message I see:
    <inx xsi:type="xsd:string">YWJj</inx>
while using WSDLDriverFactory:
    <x>YWJj</x>

That's it! Of course needs to be compatible with a byte[], that is to
say that if a type is specified it better be xsd:base64Binary. Infact,
if I rollback to pass an array like [65, 66] I can understand why I get
an IllegalArgumentExcep­tion ==> instead of xsd:base64Binary the SOAP
message contains
      <inx n2:arrayType="xsd:anyType[2]"
          xmlns:n2="http://schemas.xmlsoap.org/soap/encoding/"
          xsi:type="n2:Array">
        <item xsi:type="xsd:int">65</item>
        <item xsi:type="xsd:int">66</item>
      </inx>

Now the question is: why add_method_with_soapaction causes (sometimes
erronous) types to be passed with SOAP parameters?

Thx
AA

Now the question is: why add_method_with_soapaction causes (sometimes
erronous) types to be passed with SOAP parameters?

Possibly something to do with the following

a="hello"
puts a[0]
puts a[0..0]

104
h

that is, a single character is a byte, yet a range is a substring.

I haven't quite grokked the ruby rules around this yet.

···

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

Hello,

Sorry for the late reply.

aa wrote:

Ok, I have used an HTTP monitor to check what's going on. The result is
that working with SOAP::RPC::Driver and add_method_with_soapaction a
type is automatically attached to every parameter. Infact, in the SOAP
message I see:
    <inx xsi:type="xsd:string">YWJj</inx>
while using WSDLDriverFactory:
    <x>YWJj</x>

That's it! Of course needs to be compatible with a byte, that is to
say that if a type is specified it better be xsd:base64Binary. Infact,

[snip]

Now the question is: why add_method_with_soapaction causes (sometimes
erronous) types to be passed with SOAP parameters?

That's because without WSDL information, a driver cannot know the
defined type of passing parameters. To specify its type directly at
runtime, you can use SOAP/OM layer object (SOAP::*) such as;

  def test_echoBase64_xsd_base64Binary
    log_test
    str = "Hello (ÆüËÜžìJapanese) €³€ó€Ë€Á€Ï"
    arg = SOAP::SOAPBase64.new(str)
    arg.as_xsd # Force xsd:base64Binary instead of soap-enc:base64
    var = drv.echoBase64(arg)
    assert_equal(str, var)
  end

  def test_echoBase64_SOAP_ENC_base64
    log_test
    str = "Hello (ÆüËÜžìJapanese) €³€ó€Ë€Á€Ï"
    arg = SOAP::SOAPBase64.new(str)
    var = drv.echoBase64(arg)
    assert_equal(str, var)
  end

(excerpted from
http://dev.ctor.org/soap4r/file/trunk/test/interopR2/client.rb\)

You WSDL seems to define the parameter as xsd:base64Binary, so use the
former one.

There's also another solution. To escape from this kind of Ruby
<-(SOAP)-> Java type mapping problem, you can let Java module to
inference its type by removing type attribute for each element, if the
Java side is utilizing a WSDL.

  drv.generate_explicit_type = false

As a fact, your WSDLDriver sample does it implicitly.

Regards,
// NaHi

that is, a single character is a byte, yet a range is a substring.

I haven't quite grokked the ruby rules around this yet.

Whys www.poignantguide.net/ruby/print.html says

str = "A string is a long shelf of letters and spaces."
puts str[0] # prints 'A'

but ruby 1.8.2

irb(main):001:0> str = "A string is a long shelf of letters and spaces."
=> "A string is a long shelf of letters and spaces."
irb(main):002:0> puts str[0]
65

So I guess the behaviour changed between 1.6 and 1.8? Or why made a typo?

···

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

Here's documentation for 1.6:

http://www.ruby-doc.org/docs/ProgrammingRuby/html/ref_c_string.html#String._ob_cb

a = "hello there"
a[1] » 101

Douglas

···

On Apr 11, 2005 7:32 PM, Lyndon Samson <lyndon.samson@gmail.com> wrote:

So I guess the behaviour changed between 1.6 and 1.8? Or why made a typo?