[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[howto] pass a byte array to a web service

aastanti

4/5/2005 2:58:00 PM

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...)
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

10 Answers

Robert Klemme

4/5/2005 3:14:00 PM

0


<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',
> 'http://www.fake.com...)
> 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

4/5/2005 3:34:00 PM

0

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

Lyndon Samson

4/5/2005 4:16:00 PM

0

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.


Ernest Ellingson

4/5/2005 11:49:00 PM

0

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',
> 'http://www.fake.com...)
> 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
>
Check out this URL

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

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

aastanti

4/6/2005 6:57:00 AM

0

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

aastanti

4/6/2005 2:49:00 PM

0

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/encod...
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

Lyndon Samson

4/6/2005 3:10:00 PM

0

>
> 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.


Lyndon Samson

4/11/2005 6:32:00 PM

0

> 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.


Douglas Livingstone

4/11/2005 7:17:00 PM

0

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?

Here's documentation for 1.6:

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

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

Douglas



NAKAMURA, Hiroshi

4/15/2005 11:08:00 PM

0

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...)

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