* lib/{soap,wsdl,xsd}, test/{soap,wsdl,xsd}: imported soap4r/1.5.4.

== SOAP client and server ==

	  === for both client side and server side ===

	  * improved document/literal service support.
	    style(rpc,document)/use(encoding, literal) combination are all
	    supported.  for the detail about combination, see
	    test/soap/test_style.rb.

	  * let WSDLEncodedRegistry#soap2obj map SOAP/OM to Ruby according to
	    WSDL as well as obj2soap.  closes #70.

	  * let SOAP::Mapping::Object handle XML attribute for doc/lit service.
	    you can set/get XML attribute via accessor methods which as a name
	    'xmlattr_' prefixed (<foo name="bar"/> -> Foo#xmlattr_name).

	  === client side ===

	  * WSDLDriver capitalized name operation bug fixed.  from
	    1.5.3-ruby1.8.2, operation which has capitalized name (such as
	    KeywordSearchRequest in AWS) is defined as a method having
	    uncapitalized name. (converted with GenSupport.safemethodname
	    to handle operation name 'foo-bar').  it introduced serious
	    incompatibility; in the past, it was defined as a capitalized.
	    define capitalized method as well under that circumstance.

	  * added new factory interface 'WSDLDriverFactory#create_rpc_driver'
	    to create RPC::Driver, not WSDLDriver (RPC::Driver and WSDLDriver
	    are merged).  'WSDLDriverFactory#create_driver' still creates
	    WSDLDriver for compatibility but it warns that the method is
	    deprecated.  please use create_rpc_driver instead of create_driver.

	  * allow to use an URI object as an endpoint_url even with net/http,
	    not http-access2.

	  === server side ===

	  * added mod_ruby support to SOAP::CGIStub.  rename a CGI script
	    server.cgi to server.rb and let mod_ruby's RubyHandler handles the
	    script.  CGIStub detects if it's running under mod_ruby environment
	    or not.

	  * added fcgi support to SOAP::CGIStub.  see the sample at
	    sample/soap/calc/server.fcgi.  (almost same as server.cgi but has
	    fcgi handler at the bottom.)

	  * allow to return a SOAPFault object to respond customized SOAP fault.

	  * added the interface 'generate_explicit_type' for server side
	    (CGIStub, HTTPServer).  call 'self.generate_explicit_type = true'
	    if you want to return simplified XML even if it's rpc/encoded
	    service.

	  == WSDL ==

	  === WSDL definition ===

	  * improved XML Schema support such as extension, restriction,
	    simpleType, complexType + simpleContent, ref, length, import,
	    include.

	  * reduced "unknown element/attribute" warnings (warn only 1 time for
	    each QName).

	  * importing XSD file at schemaLocation with xsd:import.

	  === code generation from WSDL ===

	  * generator crashed when there's '-' in defined element/attribute
	    name.

	  * added ApacheMap WSDL definition.

	* sample/{soap,wsdl}: removed.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8500 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nahi 2005-05-22 13:03:38 +00:00
parent 7aea792d3b
commit eb3f829be9
211 changed files with 5997 additions and 9378 deletions

View File

@ -1,3 +1,82 @@
Sun May 22 21:54:06 2005 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* lib/{soap,wsdl,xsd}, test/{soap,wsdl,xsd}: imported soap4r/1.5.4.
== SOAP client and server ==
=== for both client side and server side ===
* improved document/literal service support.
style(rpc,document)/use(encoding, literal) combination are all
supported. for the detail about combination, see
test/soap/test_style.rb.
* let WSDLEncodedRegistry#soap2obj map SOAP/OM to Ruby according to
WSDL as well as obj2soap. closes #70.
* let SOAP::Mapping::Object handle XML attribute for doc/lit service.
you can set/get XML attribute via accessor methods which as a name
'xmlattr_' prefixed (<foo name="bar"/> -> Foo#xmlattr_name).
=== client side ===
* WSDLDriver capitalized name operation bug fixed. from
1.5.3-ruby1.8.2, operation which has capitalized name (such as
KeywordSearchRequest in AWS) is defined as a method having
uncapitalized name. (converted with GenSupport.safemethodname
to handle operation name 'foo-bar'). it introduced serious
incompatibility; in the past, it was defined as a capitalized.
define capitalized method as well under that circumstance.
* added new factory interface 'WSDLDriverFactory#create_rpc_driver'
to create RPC::Driver, not WSDLDriver (RPC::Driver and WSDLDriver
are merged). 'WSDLDriverFactory#create_driver' still creates
WSDLDriver for compatibility but it warns that the method is
deprecated. please use create_rpc_driver instead of create_driver.
* allow to use an URI object as an endpoint_url even with net/http,
not http-access2.
=== server side ===
* added mod_ruby support to SOAP::CGIStub. rename a CGI script
server.cgi to server.rb and let mod_ruby's RubyHandler handles the
script. CGIStub detects if it's running under mod_ruby environment
or not.
* added fcgi support to SOAP::CGIStub. see the sample at
sample/soap/calc/server.fcgi. (almost same as server.cgi but has
fcgi handler at the bottom.)
* allow to return a SOAPFault object to respond customized SOAP fault.
* added the interface 'generate_explicit_type' for server side
(CGIStub, HTTPServer). call 'self.generate_explicit_type = true'
if you want to return simplified XML even if it's rpc/encoded
service.
== WSDL ==
=== WSDL definition ===
* improved XML Schema support such as extension, restriction,
simpleType, complexType + simpleContent, ref, length, import,
include.
* reduced "unknown element/attribute" warnings (warn only 1 time for
each QName).
* importing XSD file at schemaLocation with xsd:import.
=== code generation from WSDL ===
* generator crashed when there's '-' in defined element/attribute
name.
* added ApacheMap WSDL definition.
* sample/{soap,wsdl}: removed.
Sun May 22 19:11:35 2005 GOTOU Yuuzou <gotoyuzo@notwork.org> Sun May 22 19:11:35 2005 GOTOU Yuuzou <gotoyuzo@notwork.org>
* ext/openssl/lib/openssl/ssl.rb (OpenSSL::SSL::SSLServer#intialize): * ext/openssl/lib/openssl/ssl.rb (OpenSSL::SSL::SSLServer#intialize):

View File

@ -1,5 +1,5 @@
# soap/baseData.rb: SOAP4R - Base type library # soap/baseData.rb: SOAP4R - Base type library
# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -56,6 +56,14 @@ module SOAPType
@extraattr = {} @extraattr = {}
end end
def inspect
if self.is_a?(XSD::NSDBase)
sprintf("#<%s:0x%x %s %s>", self.class.name, __id__, self.elename, self.type)
else
sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
end
end
def rootnode def rootnode
node = self node = self
while node = node.parent while node = node.parent
@ -399,7 +407,7 @@ public
def to_s() def to_s()
str = '' str = ''
self.each do |key, data| self.each do |key, data|
str << "#{ key }: #{ data }\n" str << "#{key}: #{data}\n"
end end
str str
end end
@ -442,6 +450,25 @@ public
@array @array
end end
def to_obj
hash = {}
proptype = {}
each do |k, v|
value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
case proptype[k]
when :single
hash[k] = [hash[k], value]
proptype[k] = :multi
when :multi
hash[k] << value
else
hash[k] = value
proptype[k] = :single
end
end
hash
end
def each def each
for i in 0..(@array.length - 1) for i in 0..(@array.length - 1)
yield(@array[i], @data[i]) yield(@array[i], @data[i])
@ -509,6 +536,10 @@ class SOAPElement
@text = text @text = text
end end
def inspect
sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
end
# Text interface. # Text interface.
attr_accessor :text attr_accessor :text
alias data text alias data text
@ -548,8 +579,19 @@ class SOAPElement
@text @text
else else
hash = {} hash = {}
proptype = {}
each do |k, v| each do |k, v|
hash[k] = v.is_a?(SOAPElement) ? v.to_obj : v.to_s value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
case proptype[k]
when :single
hash[k] = [hash[k], value]
proptype[k] = :multi
when :multi
hash[k] << value
else
hash[k] = value
proptype[k] = :single
end
end end
hash hash
end end
@ -566,20 +608,41 @@ class SOAPElement
o o
end end
def self.from_obj(hash_or_string) def self.from_obj(obj, namespace = nil)
o = SOAPElement.new(nil) o = SOAPElement.new(nil)
if hash_or_string.is_a?(Hash) case obj
hash_or_string.each do |k, v| when nil
child = self.from_obj(v) o.text = nil
child.elename = k.is_a?(XSD::QName) ? k : XSD::QName.new(nil, k.to_s) when Hash
obj.each do |elename, value|
if value.is_a?(Array)
value.each do |subvalue|
child = from_obj(subvalue, namespace)
child.elename = to_elename(elename, namespace)
o.add(child) o.add(child)
end end
else else
o.text = hash_or_string child = from_obj(value, namespace)
child.elename = to_elename(elename, namespace)
o.add(child)
end
end
else
o.text = obj.to_s
end end
o o
end end
def self.to_elename(obj, namespace = nil)
if obj.is_a?(XSD::QName)
obj
elsif /\A(.+):([^:]+)\z/ =~ obj.to_s
XSD::QName.new($1, $2)
else
XSD::QName.new(namespace, obj.to_s)
end
end
private private
def add_member(name, value) def add_member(name, value)
@ -590,18 +653,32 @@ private
value value
end end
if RUBY_VERSION > "1.7.0"
def add_accessor(name) def add_accessor(name)
methodname = name methodname = name
if self.respond_to?(methodname) if self.respond_to?(methodname)
methodname = safe_accessor_name(methodname) methodname = safe_accessor_name(methodname)
end end
sclass = class << self; self; end Mapping.define_singleton_method(self, methodname) do
sclass.__send__(:define_method, methodname, proc {
@data[@array.index(name)] @data[@array.index(name)]
}) end
sclass.__send__(:define_method, methodname + '=', proc { |value| Mapping.define_singleton_method(self, methodname + '=') do |value|
@data[@array.index(name)] = value @data[@array.index(name)] = value
}) end
end
else
def add_accessor(name)
methodname = safe_accessor_name(name)
instance_eval <<-EOS
def #{methodname}
@data[@array.index(#{name.dump})]
end
def #{methodname}=(value)
@data[@array.index(#{name.dump})] = value
end
EOS
end
end end
def safe_accessor_name(name) def safe_accessor_name(name)
@ -624,7 +701,7 @@ public
def initialize(type = nil, rank = 1, arytype = nil) def initialize(type = nil, rank = 1, arytype = nil)
super() super()
@type = type || XSD::QName.new @type = type || ValueArrayName
@rank = rank @rank = rank
@data = Array.new @data = Array.new
@sparse = false @sparse = false
@ -646,7 +723,7 @@ public
def [](*idxary) def [](*idxary)
if idxary.size != @rank if idxary.size != @rank
raise ArgumentError.new("Given #{ idxary.size } params does not match rank: #{ @rank }") raise ArgumentError.new("given #{idxary.size} params does not match rank: #{@rank}")
end end
retrieve(idxary) retrieve(idxary)
@ -656,7 +733,8 @@ public
value = idxary.slice!(-1) value = idxary.slice!(-1)
if idxary.size != @rank if idxary.size != @rank
raise ArgumentError.new("Given #{ idxary.size } params(#{ idxary }) does not match rank: #{ @rank }") raise ArgumentError.new("given #{idxary.size} params(#{idxary})" +
" does not match rank: #{@rank}")
end end
for i in 0..(idxary.size - 1) for i in 0..(idxary.size - 1)
@ -836,7 +914,7 @@ public
private private
def self.create_arytype(typename, rank) def self.create_arytype(typename, rank)
"#{ typename }[" << ',' * (rank - 1) << ']' "#{typename}[" << ',' * (rank - 1) << ']'
end end
TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$') TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$')

View File

@ -1,5 +1,5 @@
# SOAP4R - SOAP elements library # SOAP4R - SOAP elements library
# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -99,7 +99,15 @@ class SOAPBody < SOAPStruct
super(nil) super(nil)
@elename = EleBodyName @elename = EleBodyName
@encodingstyle = nil @encodingstyle = nil
add(data.elename.name, data) if data if data
if data.respond_to?(:elename)
add(data.elename.name, data)
else
data.to_a.each do |datum|
add(datum.elename.name, datum)
end
end
end
@is_fault = is_fault @is_fault = is_fault
end end
@ -129,7 +137,7 @@ class SOAPBody < SOAPStruct
end end
end end
raise SOAPParser::FormatDecodeError.new('No root element.') raise Parser::FormatDecodeError.new('no root element')
end end
end end
@ -231,8 +239,7 @@ class SOAPEnvelope < XSD::NSDBase
end end
def encode(generator, ns, attrs = {}) def encode(generator, ns, attrs = {})
SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace, SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace, SOAPNamespaceTag)
SOAPNamespaceTag)
name = ns.name(@elename) name = ns.name(@elename)
generator.encode_tag(name, attrs) generator.encode_tag(name, attrs)

View File

@ -1,5 +1,5 @@
# SOAP4R - ASP.NET EncodingStyle handler library # SOAP4R - ASP.NET EncodingStyle handler library
# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -30,7 +30,7 @@ class ASPDotNetHandler < Handler
def encode_data(generator, ns, qualified, data, parent) def encode_data(generator, ns, qualified, data, parent)
attrs = {} attrs = {}
name = if qualified and data.elename.namespace name = if qualified and data.elename.namespace
SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace) SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace, '')
ns.name(data.elename) ns.name(data.elename)
else else
data.elename.name data.elename.name
@ -61,8 +61,8 @@ class ASPDotNetHandler < Handler
yield(child, true) yield(child, true)
end end
else else
raise EncodingStyleError.new("Unknown object:#{ data } in this encodingSt raise EncodingStyleError.new(
yle.") "unknown object:#{data} in this encodingStyle")
end end
end end
@ -119,7 +119,6 @@ yle.")
end end
def decode_tag(ns, elename, attrs, parent) def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = '' @textbuf = ''
o = SOAPUnknown.new(self, elename) o = SOAPUnknown.new(self, elename)
o.parent = parent o.parent = parent
@ -190,11 +189,11 @@ yle.")
end end
when SOAPBasetype when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.") raise EncodingStyleError.new("SOAP base type must not have a child")
else else
# SOAPUnknown does not have parent. # SOAPUnknown does not have parent.
# raise EncodingStyleError.new("Illegal parent: #{ parent }.") # raise EncodingStyleError.new("illegal parent: #{parent}")
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - XML Literal EncodingStyle handler library # SOAP4R - XML Literal EncodingStyle handler library
# Copyright (C) 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -29,11 +29,22 @@ class LiteralHandler < Handler
def encode_data(generator, ns, qualified, data, parent) def encode_data(generator, ns, qualified, data, parent)
attrs = {} attrs = {}
name = if qualified and data.elename.namespace name = if qualified and data.elename.namespace
SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace) SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace, '')
ns.name(data.elename) ns.name(data.elename)
else else
data.elename.name data.elename.name
end end
data.extraattr.each do |k, v|
if k.is_a?(XSD::QName)
if k.namespace
SOAPGenerator.assign_ns(attrs, ns, k.namespace)
k = ns.name(k)
else
k = k.name
end
end
attrs[k] = v
end
case data case data
when SOAPRawString when SOAPRawString
@ -62,13 +73,14 @@ class LiteralHandler < Handler
yield(child, true) yield(child, true)
end end
when SOAPElement when SOAPElement
generator.encode_tag(name, attrs.update(data.extraattr)) generator.encode_tag(name, attrs)
generator.encode_rawstring(data.text) if data.text generator.encode_rawstring(data.text) if data.text
data.each do |key, value| data.each do |key, value|
yield(value, qualified) yield(value, qualified)
end end
else else
raise EncodingStyleError.new("Unknown object:#{ data } in this encodingStyle.") raise EncodingStyleError.new(
"unknown object:#{data} in this encodingStyle")
end end
end end
@ -78,7 +90,8 @@ class LiteralHandler < Handler
else else
data.elename.name data.elename.name
end end
cr = data.is_a?(SOAPElement) && !data.text cr = (data.is_a?(SOAPCompoundtype) or
(data.is_a?(SOAPElement) and !data.text))
generator.encode_tag_end(name, cr) generator.encode_tag_end(name, cr)
end end
@ -128,7 +141,6 @@ class LiteralHandler < Handler
end end
def decode_tag(ns, elename, attrs, parent) def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = '' @textbuf = ''
o = SOAPUnknown.new(self, elename, decode_attrs(ns, attrs)) o = SOAPUnknown.new(self, elename, decode_attrs(ns, attrs))
o.parent = parent o.parent = parent
@ -172,21 +184,19 @@ class LiteralHandler < Handler
end end
def decode_parent(parent, node) def decode_parent(parent, node)
return unless parent.node
case parent.node case parent.node
when SOAPUnknown when SOAPUnknown
newparent = parent.node.as_element newparent = parent.node.as_element
node.parent = newparent node.parent = newparent
parent.replace_node(newparent) parent.replace_node(newparent)
decode_parent(parent, node) decode_parent(parent, node)
when SOAPElement when SOAPElement
parent.node.add(node) parent.node.add(node)
node.parent = parent.node node.parent = parent.node
when SOAPStruct when SOAPStruct
parent.node.add(node.elename.name, node) parent.node.add(node.elename.name, node)
node.parent = parent.node node.parent = parent.node
when SOAPArray when SOAPArray
if node.position if node.position
parent.node[*(decode_arypos(node.position))] = node parent.node[*(decode_arypos(node.position))] = node
@ -195,13 +205,8 @@ class LiteralHandler < Handler
parent.node.add(node) parent.node.add(node)
end end
node.parent = parent.node node.parent = parent.node
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.")
else else
# SOAPUnknown does not have parent. raise EncodingStyleError.new("illegal parent: #{parent.node}")
raise EncodingStyleError.new("Illegal parent: #{ parent }.")
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - SOAP EncodingStyle handler library # SOAP4R - SOAP EncodingStyle handler library
# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -33,7 +33,7 @@ class SOAPHandler < Handler
attrs = encode_attrs(generator, ns, data, parent) attrs = encode_attrs(generator, ns, data, parent)
if parent && parent.is_a?(SOAPArray) && parent.position if parent && parent.is_a?(SOAPArray) && parent.position
attrs[ns.name(AttrPositionName)] = "[#{ parent.position.join(',') }]" attrs[ns.name(AttrPositionName)] = "[#{parent.position.join(',')}]"
end end
name = nil name = nil
@ -75,7 +75,7 @@ class SOAPHandler < Handler
end end
else else
raise EncodingStyleError.new( raise EncodingStyleError.new(
"Unknown object:#{ data } in this encodingStyle.") "unknown object:#{data} in this encodingStyle")
end end
end end
@ -156,7 +156,6 @@ class SOAPHandler < Handler
end end
def decode_tag(ns, elename, attrs, parent) def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = '' @textbuf = ''
is_nil, type, arytype, root, offset, position, href, id, extraattr = is_nil, type, arytype, root, offset, position, href, id, extraattr =
decode_attrs(ns, attrs) decode_attrs(ns, attrs)
@ -227,6 +226,7 @@ class SOAPHandler < Handler
end end
def decode_parent(parent, node) def decode_parent(parent, node)
return unless parent.node
case parent.node case parent.node
when SOAPUnknown when SOAPUnknown
newparent = parent.node.as_struct newparent = parent.node.as_struct
@ -247,10 +247,8 @@ class SOAPHandler < Handler
parent.node.add(node) parent.node.add(node)
end end
node.parent = parent.node node.parent = parent.node
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.")
else else
raise EncodingStyleError.new("Illegal parent: #{ parent.node }.") raise EncodingStyleError.new("illegal parent: #{parent.node}")
end end
end end
@ -266,7 +264,7 @@ private
def create_arytype(ns, data) def create_arytype(ns, data)
XSD::QName.new(data.arytype.namespace, XSD::QName.new(data.arytype.namespace,
content_typename(data.arytype.name) + "[#{ data.size.join(',') }]") content_typename(data.arytype.name) + "[#{data.size.join(',')}]")
end end
def encode_attrs(generator, ns, data, parent) def encode_attrs(generator, ns, data, parent)
@ -383,7 +381,7 @@ private
def decode_definedtype(elename, typename, typedef, arytypestr) def decode_definedtype(elename, typename, typedef, arytypestr)
unless typedef unless typedef
raise EncodingStyleError.new("Unknown type '#{ typename }'.") raise EncodingStyleError.new("unknown type '#{typename}'")
end end
if typedef.is_a?(::WSDL::XMLSchema::SimpleType) if typedef.is_a?(::WSDL::XMLSchema::SimpleType)
decode_defined_simpletype(elename, typename, typedef, arytypestr) decode_defined_simpletype(elename, typename, typedef, arytypestr)
@ -421,6 +419,10 @@ private
end end
o.definedtype = typedef o.definedtype = typedef
return o return o
when :TYPE_EMPTY
o = SOAPNil.decode(elename)
o.definedtype = typedef
return o
else else
raise RuntimeError.new( raise RuntimeError.new(
"Unknown kind of complexType: #{typedef.compoundtype}") "Unknown kind of complexType: #{typedef.compoundtype}")
@ -509,7 +511,7 @@ private
case qname.name case qname.name
when XSD::NilLiteral when XSD::NilLiteral
is_nil = NilLiteralMap[value] or is_nil = NilLiteralMap[value] or
raise EncodingStyleError.new("Cannot accept attribute value: #{ value } as the value of xsi:#{ XSD::NilLiteral } (expected 'true', 'false', '1', or '0').") raise EncodingStyleError.new("cannot accept attribute value: #{value} as the value of xsi:#{XSD::NilLiteral} (expected 'true', 'false', '1', or '0')")
next next
when XSD::AttrType when XSD::AttrType
type = value type = value
@ -523,7 +525,7 @@ private
when AttrRoot when AttrRoot
root = RootLiteralMap[value] or root = RootLiteralMap[value] or
raise EncodingStyleError.new( raise EncodingStyleError.new(
"Illegal root attribute value: #{ value }.") "illegal root attribute value: #{value}")
next next
when AttrOffset when AttrOffset
offset = value offset = value
@ -578,7 +580,7 @@ private
ref.__setobj__(o) ref.__setobj__(o)
false false
else else
raise EncodingStyleError.new("Unresolved reference: #{ ref.refid }.") raise EncodingStyleError.new("unresolved reference: #{ref.refid}")
end end
} }
count -= 1 count -= 1

View File

@ -175,7 +175,12 @@ public
def self.assign_ns(attrs, ns, namespace, tag = nil) def self.assign_ns(attrs, ns, namespace, tag = nil)
if namespace and !ns.assigned?(namespace) if namespace and !ns.assigned?(namespace)
tag = ns.assign(namespace, tag) tag = ns.assign(namespace, tag)
attrs['xmlns:' << tag] = namespace if tag == ''
attr = 'xmlns'
else
attr = "xmlns:#{tag}"
end
attrs[attr] = namespace
end end
end end

View File

@ -18,6 +18,12 @@ class HandlerSet
@store = XSD::NamedElements.new @store = XSD::NamedElements.new
end end
def dup
obj = HandlerSet.new
obj.store = @store.dup
obj
end
def add(handler) def add(handler)
@store << handler @store << handler
end end
@ -51,6 +57,12 @@ class HandlerSet
end end
end end
end end
protected
def store=(store)
@store = store
end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - SOAP Simple header item handler # SOAP4R - SOAP Simple header item handler
# Copyright (C) 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -30,7 +30,7 @@ class SimpleHandler < SOAP::Header::Handler
def on_outbound def on_outbound
h = on_simple_outbound h = on_simple_outbound
h ? SOAPElement.from_obj(h) : nil h ? SOAPElement.from_obj(h, elename.namespace) : nil
end end
def on_inbound(header, mustunderstand) def on_inbound(header, mustunderstand)

View File

@ -0,0 +1,116 @@
# SOAP4R - HTTP config loader.
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/property'
module SOAP
module HTTPConfigLoader
module_function
def set_options(client, options)
client.proxy = options["proxy"]
options.add_hook("proxy") do |key, value|
client.proxy = value
end
client.no_proxy = options["no_proxy"]
options.add_hook("no_proxy") do |key, value|
client.no_proxy = value
end
if client.respond_to?(:protocol_version=)
client.protocol_version = options["protocol_version"]
options.add_hook("protocol_version") do |key, value|
client.protocol_version = value
end
end
ssl_config = options["ssl_config"] ||= ::SOAP::Property.new
set_ssl_config(client, ssl_config)
ssl_config.add_hook(true) do |key, value|
set_ssl_config(client, ssl_config)
end
basic_auth = options["basic_auth"] ||= ::SOAP::Property.new
set_basic_auth(client, basic_auth)
basic_auth.add_hook do |key, value|
set_basic_auth(client, basic_auth)
end
options.add_hook("connect_timeout") do |key, value|
client.connect_timeout = value
end
options.add_hook("send_timeout") do |key, value|
client.send_timeout = value
end
options.add_hook("receive_timeout") do |key, value|
client.receive_timeout = value
end
end
def set_basic_auth(client, basic_auth)
basic_auth.values.each do |url, userid, passwd|
client.set_basic_auth(url, userid, passwd)
end
end
def set_ssl_config(client, ssl_config)
ssl_config.each do |key, value|
cfg = client.ssl_config
case key
when 'client_cert'
cfg.client_cert = cert_from_file(value)
when 'client_key'
cfg.client_key = key_from_file(value)
when 'client_ca'
cfg.client_ca = value
when 'ca_path'
cfg.set_trust_ca(value)
when 'ca_file'
cfg.set_trust_ca(value)
when 'crl'
cfg.set_crl(value)
when 'verify_mode'
cfg.verify_mode = ssl_config_int(value)
when 'verify_depth'
cfg.verify_depth = ssl_config_int(value)
when 'options'
cfg.options = value
when 'ciphers'
cfg.ciphers = value
when 'verify_callback'
cfg.verify_callback = value
when 'cert_store'
cfg.cert_store = value
else
raise ArgumentError.new("unknown ssl_config property #{key}")
end
end
end
def ssl_config_int(value)
if value.nil? or value.to_s.empty?
nil
else
begin
Integer(value)
rescue ArgumentError
::SOAP::Property::Util.const_from_name(value.to_s)
end
end
end
def cert_from_file(filename)
OpenSSL::X509::Certificate.new(File.open(filename) { |f| f.read })
end
def key_from_file(filename)
OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read })
end
end
end

View File

@ -37,22 +37,26 @@ class Factory
end end
def setiv2soap(node, obj, map) def setiv2soap(node, obj, map)
if obj.class.class_variables.include?('@@schema_element')
obj.class.class_eval('@@schema_element').each do |name, info|
type, qname = info
if qname
elename = qname.name
else
elename = Mapping.name2elename(name)
end
node.add(elename,
Mapping._obj2soap(obj.instance_variable_get('@' + name), map))
end
else
# should we sort instance_variables? # should we sort instance_variables?
obj.instance_variables.each do |var| obj.instance_variables.each do |var|
name = var.sub(/^@/, '') name = var.sub(/^@/, '')
node.add(Mapping.name2elename(name), elename = Mapping.name2elename(name)
node.add(elename,
Mapping._obj2soap(obj.instance_variable_get(var), map)) Mapping._obj2soap(obj.instance_variable_get(var), map))
end end
end end
# It breaks Thread.current[:SOAPMarshalDataKey].
def mark_marshalled_obj(obj, soap_obj)
Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj
end
# It breaks Thread.current[:SOAPMarshalDataKey].
def mark_unmarshalled_obj(node, obj)
Thread.current[:SOAPMarshalDataKey][node.id] = obj
end end
private private
@ -68,7 +72,7 @@ private
node.each do |name, value| node.each do |name, value|
vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
end end
Mapping.set_instance_vars(obj, vars) Mapping.set_attributes(obj, vars)
end end
end end
@ -156,20 +160,14 @@ class DateTimeFactory_ < Factory
end end
def soap2obj(obj_class, node, info, map) def soap2obj(obj_class, node, info, map)
obj = nil if node.respond_to?(:to_obj)
if obj_class == Time obj = node.to_obj(obj_class)
obj = node.to_time return false if obj.nil?
if obj.nil? mark_unmarshalled_obj(node, obj)
# Is out of range as a Time return true, obj
return false
end
elsif obj_class == Date
obj = node.data
else else
return false return false
end end
mark_unmarshalled_obj(node, obj)
return true, obj
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - Ruby type mapping utility. # SOAP4R - Ruby type mapping utility.
# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000, 2001, 2003-2005 NAKAMURA Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -23,10 +23,12 @@ module Mapping
# TraverseSupport breaks Thread.current[:SOAPMarshalDataKey]. # TraverseSupport breaks Thread.current[:SOAPMarshalDataKey].
module TraverseSupport module TraverseSupport
def mark_marshalled_obj(obj, soap_obj) def mark_marshalled_obj(obj, soap_obj)
raise if obj.nil?
Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj
end end
def mark_unmarshalled_obj(node, obj) def mark_unmarshalled_obj(node, obj)
return if obj.nil?
# node.id is not Object#id but SOAPReference#id # node.id is not Object#id but SOAPReference#id
Thread.current[:SOAPMarshalDataKey][node.id] = obj Thread.current[:SOAPMarshalDataKey][node.id] = obj
end end
@ -41,10 +43,10 @@ module Mapping
soap_obj soap_obj
end end
def self.soap2obj(node, registry = nil) def self.soap2obj(node, registry = nil, klass = nil)
registry ||= Mapping::DefaultRegistry registry ||= Mapping::DefaultRegistry
Thread.current[:SOAPMarshalDataKey] = {} Thread.current[:SOAPMarshalDataKey] = {}
obj = _soap2obj(node, registry) obj = _soap2obj(node, registry, klass)
Thread.current[:SOAPMarshalDataKey] = nil Thread.current[:SOAPMarshalDataKey] = nil
obj obj
end end
@ -107,21 +109,21 @@ module Mapping
elsif registry elsif registry
registry.obj2soap(obj, type) registry.obj2soap(obj, type)
else else
raise MappingError.new("No mapping registry given.") raise MappingError.new("no mapping registry given")
end end
end end
def self._soap2obj(node, registry) def self._soap2obj(node, registry, klass = nil)
if node.is_a?(SOAPReference) if node.is_a?(SOAPReference)
target = node.__getobj__ target = node.__getobj__
# target.id is not Object#id but SOAPReference#id # target.id is not Object#id but SOAPReference#id
if referent = Thread.current[:SOAPMarshalDataKey][target.id] if referent = Thread.current[:SOAPMarshalDataKey][target.id]
return referent return referent
else else
return _soap2obj(target, registry) return _soap2obj(target, registry, klass)
end end
end end
return registry.soap2obj(node) return registry.soap2obj(node, klass)
end end
if Object.respond_to?(:allocate) if Object.respond_to?(:allocate)
@ -157,29 +159,6 @@ module Mapping
end end
end end
unless Object.respond_to?(:instance_variable_get)
class Object
def instance_variable_get(ivarname)
instance_eval(ivarname)
end
def instance_variable_set(ivarname, value)
instance_eval("#{ivarname} = value")
end
end
end
def self.set_instance_vars(obj, values)
values.each do |name, value|
setter = name + "="
if obj.respond_to?(setter)
obj.__send__(setter, value)
else
obj.instance_variable_set('@' + name, value)
end
end
end
# Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here. # Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here.
# Caution: '.' is not allowed here. # Caution: '.' is not allowed here.
# To follow XML spec., it should be NCName. # To follow XML spec., it should be NCName.
@ -198,28 +177,51 @@ module Mapping
} }
end end
def self.class_from_name(name) def self.const_from_name(name, lenient = false)
if /^[A-Z]/ !~ name const = ::Object
name.sub(/\A::/, '').split('::').each do |const_str|
if XSD::CodeGen::GenSupport.safeconstname?(const_str)
if const.const_defined?(const_str)
const = const.const_get(const_str)
next
end
elsif lenient
const_str = XSD::CodeGen::GenSupport.safeconstname(const_str)
if const.const_defined?(const_str)
const = const.const_get(const_str)
next
end
end
return nil return nil
end end
klass = ::Object const
name.split('::').each do |klass_str| end
if klass.const_defined?(klass_str)
klass = klass.const_get(klass_str) def self.class_from_name(name, lenient = false)
const = const_from_name(name, lenient)
if const.is_a?(::Class)
const
else else
return nil nil
end end
end end
klass
def self.module_from_name(name, lenient = false)
const = const_from_name(name, lenient)
if const.is_a?(::Module)
const
else
nil
end
end end
def self.class2qname(klass) def self.class2qname(klass)
name = if klass.class_variables.include?("@@schema_type") name = if klass.class_variables.include?('@@schema_type')
klass.class_eval('@@schema_type') klass.class_eval('@@schema_type')
else else
nil nil
end end
namespace = if klass.class_variables.include?("@@schema_ns") namespace = if klass.class_variables.include?('@@schema_ns')
klass.class_eval('@@schema_ns') klass.class_eval('@@schema_ns')
else else
nil nil
@ -250,19 +252,75 @@ module Mapping
end end
end end
def self.find_attribute(obj, attr_name) def self.define_singleton_method(obj, name, &block)
sclass = (class << obj; self; end)
sclass.__send__(:define_method, name, &block)
end
def self.get_attribute(obj, attr_name)
if obj.is_a?(::Hash) if obj.is_a?(::Hash)
obj[attr_name] || obj[attr_name.intern] obj[attr_name] || obj[attr_name.intern]
else else
name = ::XSD::CodeGen::GenSupport.safevarname(attr_name) name = XSD::CodeGen::GenSupport.safevarname(attr_name)
if obj.respond_to?(name) if obj.instance_variables.include?('@' + name)
obj.__send__(name)
else
obj.instance_variable_get('@' + name) obj.instance_variable_get('@' + name)
elsif ((obj.is_a?(::Struct) or obj.is_a?(Marshallable)) and
obj.respond_to?(name))
obj.__send__(name)
end end
end end
end end
def self.set_attributes(obj, values)
if obj.is_a?(::SOAP::Mapping::Object)
values.each do |attr_name, value|
obj.__add_xmlele_value(attr_name, value)
end
else
values.each do |attr_name, value|
name = XSD::CodeGen::GenSupport.safevarname(attr_name)
setter = name + "="
if obj.respond_to?(setter)
obj.__send__(setter, value)
else
obj.instance_variable_set('@' + name, value)
begin
define_attr_accessor(obj, name,
proc { instance_variable_get('@' + name) },
proc { |value| instance_variable_set('@' + name, value) })
rescue TypeError
# singleton class may not exist (e.g. Float)
end
end
end
end
end
def self.define_attr_accessor(obj, name, getterproc, setterproc = nil)
define_singleton_method(obj, name, &getterproc)
define_singleton_method(obj, name + '=', &setterproc) if setterproc
end
def self.schema_element_definition(klass)
return nil unless klass.class_variables.include?('@@schema_element')
elements = {}
as_array = []
klass.class_eval('@@schema_element').each do |varname, definition|
class_name, name = definition
if /\[\]$/ =~ class_name
class_name = class_name.sub(/\[\]$/, '')
as_array << class_name
end
elements[name ? name.name : varname] = class_name
end
[elements, as_array]
end
def self.schema_attribute_definition(klass)
return nil unless klass.class_variables.include?('@@schema_attribute')
klass.class_eval('@@schema_attribute')
end
class << Mapping class << Mapping
private private
def add_md_ary(md_ary, ary, indices, registry) def add_md_ary(md_ary, ary, indices, registry)

View File

@ -64,50 +64,105 @@ end
# For anyType object: SOAP::Mapping::Object not ::Object # For anyType object: SOAP::Mapping::Object not ::Object
class Object; include Marshallable class Object; include Marshallable
def initialize def initialize
@__soap_value_type = {} @__xmlele_type = {}
@__soap_value = {} @__xmlele = []
@__xmlattr = {}
end end
def [](name) def inspect
@__soap_value[name] sprintf("#<%s:0x%x%s>", self.class.name, __id__,
@__xmlele.collect { |name, value| " #{name}=#{value.inspect}" }.join)
end end
def []=(name, value) def __xmlattr
@__soap_value[name] = value @__xmlattr
end end
def __soap_set_property(name, value) def __xmlele
unless @__soap_value.key?(name) @__xmlele
__define_attr_accessor(name)
end
__soap_set_property_value(name, value)
end end
private def [](qname)
unless qname.is_a?(XSD::QName)
qname = XSD::QName.new(nil, qname)
end
@__xmlele.each do |k, v|
return v if k == qname
end
nil
end
def __soap_set_property_value(name, value) def []=(qname, value)
org = self[name] unless qname.is_a?(XSD::QName)
case @__soap_value_type[name] qname = XSD::QName.new(nil, qname)
when :single end
self[name] = [org, value] found = false
@__soap_value_type[name] = :multi @__xmlele.each do |pair|
when :multi if pair[0] == qname
org << value found = true
pair[1] = value
end
end
unless found
__define_attr_accessor(qname)
@__xmlele << [qname, value]
end
@__xmlele_type[qname] = :single
end
def __add_xmlele_value(qname, value)
found = false
@__xmlele.map! do |k, v|
if k == qname
found = true
[k, __set_xmlele_value(k, v, value)]
else else
self[name] = value [k, v]
@__soap_value_type[name] = :single end
end
unless found
__define_attr_accessor(qname)
@__xmlele << [qname, value]
@__xmlele_type[qname] = :single
end end
value value
end end
def __define_attr_accessor(name) private
sclass = class << self; self; end
sclass.__send__(:define_method, name, proc { if RUBY_VERSION > "1.7.0"
self[name] def __define_attr_accessor(qname)
}) name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
sclass.__send__(:define_method, name + '=', proc { |value| Mapping.define_attr_accessor(self, name,
self[name] = value proc { self[qname] },
}) proc { |value| self[qname] = value })
end
else
def __define_attr_accessor(qname)
name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
instance_eval <<-EOS
def #{name}
self[#{qname.dump}]
end
def #{name}=(value)
self[#{qname.dump}] = value
end
EOS
end
end
def __set_xmlele_value(key, org, value)
case @__xmlele_type[key]
when :multi
org << value
org
when :single
@__xmlele_type[key] = :multi
[org, value]
else
raise RuntimeError.new("unknown type")
end
end end
end end
@ -123,7 +178,7 @@ class Registry
@registry = registry @registry = registry
end end
def obj2soap(obj, type_qname = nil) def obj2soap(obj)
klass = obj.class klass = obj.class
if map = @obj2soap[klass] if map = @obj2soap[klass]
map.each do |soap_class, factory, info| map.each do |soap_class, factory, info|
@ -131,7 +186,10 @@ class Registry
return ret if ret return ret if ret
end end
end end
ancestors = klass.ancestors[1..-3] # except itself, Object and Kernel ancestors = klass.ancestors
ancestors.delete(klass)
ancestors.delete(::Object)
ancestors.delete(::Kernel)
ancestors.each do |klass| ancestors.each do |klass|
if map = @obj2soap[klass] if map = @obj2soap[klass]
map.each do |soap_class, factory, info| map.each do |soap_class, factory, info|
@ -145,10 +203,10 @@ class Registry
nil nil
end end
def soap2obj(node) def soap2obj(node, klass = nil)
klass = node.class if map = @soap2obj[node.class]
if map = @soap2obj[klass]
map.each do |obj_class, factory, info| map.each do |obj_class, factory, info|
next if klass and obj_class != klass
conv, obj = factory.soap2obj(obj_class, node, info, @registry) conv, obj = factory.soap2obj(obj_class, node, info, @registry)
return true, obj if conv return true, obj if conv
end end
@ -177,11 +235,13 @@ class Registry
end end
def find_mapped_soap_class(target_obj_class) def find_mapped_soap_class(target_obj_class)
@obj2soap[target_obj_class][0] map = @obj2soap[target_obj_class]
map.empty? ? nil : map[0][1]
end end
def find_mapped_obj_class(target_soap_class) def find_mapped_obj_class(target_soap_class)
@soap2obj[target_soap_class][0] map = @soap2obj[target_soap_class]
map.empty? ? nil : map[0][0]
end end
end end
@ -202,7 +262,6 @@ class Registry
[::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory], [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory],
[::String, ::SOAP::SOAPString, StringFactory], [::String, ::SOAP::SOAPString, StringFactory],
[::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory], [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Date, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Date, ::SOAP::SOAPDate, DateTimeFactory], [::Date, ::SOAP::SOAPDate, DateTimeFactory],
[::Time, ::SOAP::SOAPDateTime, DateTimeFactory], [::Time, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Time, ::SOAP::SOAPTime, DateTimeFactory], [::Time, ::SOAP::SOAPTime, DateTimeFactory],
@ -266,7 +325,6 @@ class Registry
[::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory], [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory],
[::String, ::SOAP::SOAPString, StringFactory], [::String, ::SOAP::SOAPString, StringFactory],
[::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory], [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Date, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Date, ::SOAP::SOAPDate, DateTimeFactory], [::Date, ::SOAP::SOAPDate, DateTimeFactory],
[::Time, ::SOAP::SOAPDateTime, DateTimeFactory], [::Time, ::SOAP::SOAPDateTime, DateTimeFactory],
[::Time, ::SOAP::SOAPTime, DateTimeFactory], [::Time, ::SOAP::SOAPTime, DateTimeFactory],
@ -354,17 +412,17 @@ class Registry
end end
alias set add alias set add
# This mapping registry ignores type hint. # general Registry ignores type_qname
def obj2soap(obj, type_qname = nil) def obj2soap(obj, type_qname = nil)
soap = _obj2soap(obj, type_qname) soap = _obj2soap(obj)
if @allow_original_mapping if @allow_original_mapping
addextend2soap(soap, obj) addextend2soap(soap, obj)
end end
soap soap
end end
def soap2obj(node) def soap2obj(node, klass = nil)
obj = _soap2obj(node) obj = _soap2obj(node, klass)
if @allow_original_mapping if @allow_original_mapping
addextend2obj(obj, node.extraattr[RubyExtendName]) addextend2obj(obj, node.extraattr[RubyExtendName])
addiv2obj(obj, node.extraattr[RubyIVarName]) addiv2obj(obj, node.extraattr[RubyIVarName])
@ -382,7 +440,7 @@ class Registry
private private
def _obj2soap(obj, type_qname) def _obj2soap(obj)
ret = nil ret = nil
if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray) if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray)
obj.replace do |ele| obj.replace do |ele|
@ -393,7 +451,7 @@ private
return obj return obj
end end
begin begin
ret = @map.obj2soap(obj, type_qname) || ret = @map.obj2soap(obj) ||
@default_factory.obj2soap(nil, obj, nil, self) @default_factory.obj2soap(nil, obj, nil, self)
return ret if ret return ret if ret
rescue MappingError rescue MappingError
@ -408,12 +466,12 @@ private
end end
# Might return nil as a mapping result. # Might return nil as a mapping result.
def _soap2obj(node) def _soap2obj(node, klass = nil)
if node.extraattr.key?(RubyTypeName) if node.extraattr.key?(RubyTypeName)
conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self) conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self)
return obj if conv return obj if conv
else else
conv, obj = @map.soap2obj(node) conv, obj = @map.soap2obj(node, klass)
return obj if conv return obj if conv
conv, obj = @default_factory.soap2obj(nil, node, nil, self) conv, obj = @default_factory.soap2obj(nil, node, nil, self)
return obj if conv return obj if conv
@ -435,14 +493,14 @@ private
attr.__getobj__.each do |name, value| attr.__getobj__.each do |name, value|
vars[name] = Mapping._soap2obj(value, self) vars[name] = Mapping._soap2obj(value, self)
end end
Mapping.set_instance_vars(obj, vars) Mapping.set_attributes(obj, vars)
end end
if RUBY_VERSION >= '1.8.0' if RUBY_VERSION >= '1.8.0'
def addextend2obj(obj, attr) def addextend2obj(obj, attr)
return unless attr return unless attr
attr.split(/ /).reverse_each do |mstr| attr.split(/ /).reverse_each do |mstr|
obj.extend(Mapping.class_from_name(mstr)) obj.extend(Mapping.module_from_name(mstr))
end end
end end
else else
@ -450,8 +508,8 @@ private
def addextend2obj(obj, attr) def addextend2obj(obj, attr)
return unless attr return unless attr
attr.split(/ /).reverse_each do |mstr| attr.split(/ /).reverse_each do |mstr|
m = Mapping.class_from_name(mstr) m = Mapping.module_from_name(mstr)
obj.extend(m) if m.class == Module obj.extend(m)
end end
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - Ruby type mapping factory. # SOAP4R - Ruby type mapping factory.
# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -168,7 +168,7 @@ class RubytypeFactory < Factory
return nil return nil
end end
if obj.to_s[0] == ?# if obj.to_s[0] == ?#
raise TypeError.new("can't dump anonymous class #{ obj }") raise TypeError.new("can't dump anonymous class #{obj}")
end end
param = SOAPStruct.new(TYPE_CLASS) param = SOAPStruct.new(TYPE_CLASS)
mark_marshalled_obj(obj, param) mark_marshalled_obj(obj, param)
@ -179,7 +179,7 @@ class RubytypeFactory < Factory
return nil return nil
end end
if obj.to_s[0] == ?# if obj.to_s[0] == ?#
raise TypeError.new("can't dump anonymous module #{ obj }") raise TypeError.new("can't dump anonymous module #{obj}")
end end
param = SOAPStruct.new(TYPE_MODULE) param = SOAPStruct.new(TYPE_MODULE)
mark_marshalled_obj(obj, param) mark_marshalled_obj(obj, param)
@ -222,7 +222,12 @@ class RubytypeFactory < Factory
when ::SOAP::Mapping::Object when ::SOAP::Mapping::Object
param = SOAPStruct.new(XSD::AnyTypeName) param = SOAPStruct.new(XSD::AnyTypeName)
mark_marshalled_obj(obj, param) mark_marshalled_obj(obj, param)
addiv2soapattr(param, obj, map) obj.__xmlele.each do |key, value|
param.add(key.name, Mapping._obj2soap(value, map))
end
obj.__xmlattr.each do |key, value|
param.extraattr[key] = value
end
when ::Exception when ::Exception
typestr = Mapping.name2elename(obj.class.to_s) typestr = Mapping.name2elename(obj.class.to_s)
param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr)) param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr))
@ -258,12 +263,12 @@ private
def unknownobj2soap(soap_class, obj, info, map) def unknownobj2soap(soap_class, obj, info, map)
if obj.class.name.empty? if obj.class.name.empty?
raise TypeError.new("can't dump anonymous class #{ obj }") raise TypeError.new("can't dump anonymous class #{obj}")
end end
singleton_class = class << obj; self; end singleton_class = class << obj; self; end
if !singleton_methods_true(obj).empty? or if !singleton_methods_true(obj).empty? or
!singleton_class.instance_variables.empty? !singleton_class.instance_variables.empty?
raise TypeError.new("singleton can't be dumped #{ obj }") raise TypeError.new("singleton can't be dumped #{obj}")
end end
if !(singleton_class.ancestors - obj.class.ancestors).empty? if !(singleton_class.ancestors - obj.class.ancestors).empty?
typestr = Mapping.name2elename(obj.class.to_s) typestr = Mapping.name2elename(obj.class.to_s)
@ -378,7 +383,11 @@ private
obj = klass.new obj = klass.new
mark_unmarshalled_obj(node, obj) mark_unmarshalled_obj(node, obj)
node.each do |name, value| node.each do |name, value|
obj.__soap_set_property(name, Mapping._soap2obj(value, map)) obj.__add_xmlele_value(XSD::QName.new(nil, name),
Mapping._soap2obj(value, map))
end
unless node.extraattr.empty?
obj.instance_variable_set('@__xmlattr', node.extraattr)
end end
return true, obj return true, obj
else else
@ -387,7 +396,12 @@ private
end end
def unknowntype2obj(node, info, map) def unknowntype2obj(node, info, map)
if node.is_a?(SOAPStruct) case node
when SOAPBasetype
return true, node.data
when SOAPArray
return @array_factory.soap2obj(Array, node, info, map)
when SOAPStruct
obj = unknownstruct2obj(node, info, map) obj = unknownstruct2obj(node, info, map)
return true, obj if obj return true, obj if obj
if !@allow_untyped_struct if !@allow_untyped_struct
@ -406,6 +420,9 @@ private
end end
typestr = Mapping.elename2name(node.type.name) typestr = Mapping.elename2name(node.type.name)
klass = Mapping.class_from_name(typestr) klass = Mapping.class_from_name(typestr)
if klass.nil? and @allow_untyped_struct
klass = Mapping.class_from_name(typestr, true) # lenient
end
if klass.nil? if klass.nil?
return nil return nil
end end
@ -414,7 +431,13 @@ private
end end
klass_type = Mapping.class2qname(klass) klass_type = Mapping.class2qname(klass)
return nil unless node.type.match(klass_type) return nil unless node.type.match(klass_type)
obj = nil
begin
obj = Mapping.create_empty_object(klass) obj = Mapping.create_empty_object(klass)
rescue
# type name "data" tries Data.new which raises TypeError
nil
end
mark_unmarshalled_obj(node, obj) mark_unmarshalled_obj(node, obj)
setiv2obj(obj, node, map) setiv2obj(obj, node, map)
obj obj

View File

@ -1,11 +1,13 @@
# SOAP4R - WSDL encoded mapping registry. # SOAP4R - WSDL encoded mapping registry.
# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version. # either the dual license version in 2003, or any later version.
require 'xsd/qname'
require 'xsd/namedelements'
require 'soap/baseData' require 'soap/baseData'
require 'soap/mapping/mapping' require 'soap/mapping/mapping'
require 'soap/mapping/typeMap' require 'soap/mapping/typeMap'
@ -15,35 +17,33 @@ module SOAP
module Mapping module Mapping
class WSDLEncodedRegistry class WSDLEncodedRegistry < Registry
include TraverseSupport include TraverseSupport
attr_reader :definedelements
attr_reader :definedtypes attr_reader :definedtypes
attr_accessor :excn_handler_obj2soap attr_accessor :excn_handler_obj2soap
attr_accessor :excn_handler_soap2obj
def initialize(definedtypes, config = {}) def initialize(definedtypes = XSD::NamedElements::Empty)
@definedtypes = definedtypes @definedtypes = definedtypes
@config = config # @definedelements = definedelements needed?
@excn_handler_obj2soap = nil @excn_handler_obj2soap = nil
@excn_handler_soap2obj = nil
# For mapping AnyType element. # For mapping AnyType element.
@rubytype_factory = RubytypeFactory.new( @rubytype_factory = RubytypeFactory.new(
:allow_untyped_struct => true, :allow_untyped_struct => true,
:allow_original_mapping => true :allow_original_mapping => true
) )
@schema_element_cache = {}
end end
def obj2soap(obj, type_qname = nil) def obj2soap(obj, qname = nil)
soap_obj = nil soap_obj = nil
if obj.nil? if type = @definedtypes[qname]
soap_obj = SOAPNil.new soap_obj = obj2typesoap(obj, type)
elsif type_qname.nil? or type_qname == XSD::AnyTypeName else
soap_obj = @rubytype_factory.obj2soap(nil, obj, nil, self) soap_obj = any2soap(obj, qname)
elsif obj.is_a?(XSD::NSDBase)
soap_obj = soap2soap(obj, type_qname)
elsif type = @definedtypes[type_qname]
soap_obj = obj2type(obj, type)
elsif (type = TypeMap[type_qname])
soap_obj = base2soap(obj, type)
end end
return soap_obj if soap_obj return soap_obj if soap_obj
if @excn_handler_obj2soap if @excn_handler_obj2soap
@ -52,15 +52,46 @@ class WSDLEncodedRegistry
} }
return soap_obj if soap_obj return soap_obj if soap_obj
end end
raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") if qname
raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
else
raise MappingError.new("cannot map #{obj.class.name} to SOAP/OM")
end
end end
def soap2obj(node) # map anything for now: must refer WSDL while mapping. [ToDo]
raise RuntimeError.new("#{ self } is for obj2soap only.") def soap2obj(node, obj_class = nil)
begin
return any2obj(node, obj_class)
rescue MappingError
end
if @excn_handler_soap2obj
begin
return @excn_handler_soap2obj.call(node) { |yield_node|
Mapping._soap2obj(yield_node, self)
}
rescue Exception
end
end
raise MappingError.new("cannot map #{node.type.name} to Ruby object")
end end
private private
def any2soap(obj, qname)
if obj.nil?
SOAPNil.new
elsif qname.nil? or qname == XSD::AnyTypeName
@rubytype_factory.obj2soap(nil, obj, nil, self)
elsif obj.is_a?(XSD::NSDBase)
soap2soap(obj, qname)
elsif (type = TypeMap[qname])
base2soap(obj, type)
else
nil
end
end
def soap2soap(obj, type_qname) def soap2soap(obj, type_qname)
if obj.is_a?(SOAPBasetype) if obj.is_a?(SOAPBasetype)
obj obj
@ -82,25 +113,22 @@ private
end end
end end
def obj2type(obj, type) def obj2typesoap(obj, type)
if type.is_a?(::WSDL::XMLSchema::SimpleType) if type.is_a?(::WSDL::XMLSchema::SimpleType)
simple2soap(obj, type) simpleobj2soap(obj, type)
else else
complex2soap(obj, type) complexobj2soap(obj, type)
end end
end end
def simple2soap(obj, type) def simpleobj2soap(obj, type)
o = base2soap(obj, TypeMap[type.base])
if type.restriction.enumeration.empty?
STDERR.puts("#{type.name}: simpleType which is not enum type not supported.")
return o
end
type.check_lexical_format(obj) type.check_lexical_format(obj)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
o = base2soap(obj, TypeMap[type.base])
o o
end end
def complex2soap(obj, type) def complexobj2soap(obj, type)
case type.compoundtype case type.compoundtype
when :TYPE_STRUCT when :TYPE_STRUCT
struct2soap(obj, type.name, type) struct2soap(obj, type.name, type)
@ -108,8 +136,13 @@ private
array2soap(obj, type.name, type) array2soap(obj, type.name, type)
when :TYPE_MAP when :TYPE_MAP
map2soap(obj, type.name, type) map2soap(obj, type.name, type)
when :TYPE_SIMPLE
simpleobj2soap(obj, type.simplecontent)
when :TYPE_EMPTY
raise MappingError.new("should be empty") unless obj.nil?
SOAPNil.new
else else
raise MappingError.new("Unknown compound type: #{ type.compoundtype }") raise MappingError.new("unknown compound type: #{type.compoundtype}")
end end
end end
@ -126,28 +159,36 @@ private
end end
def struct2soap(obj, type_qname, type) def struct2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
soap_obj = SOAPStruct.new(type_qname) soap_obj = SOAPStruct.new(type_qname)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj) mark_marshalled_obj(obj, soap_obj)
elements2soap(obj, soap_obj, type.content.elements) elements2soap(obj, soap_obj, type.content.elements)
end
soap_obj soap_obj
end end
def array2soap(obj, type_qname, type) def array2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
arytype = type.child_type arytype = type.child_type
soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj) mark_marshalled_obj(obj, soap_obj)
obj.each do |item| obj.each do |item|
soap_obj.add(Mapping._obj2soap(item, self, arytype)) soap_obj.add(Mapping._obj2soap(item, self, arytype))
end end
end
soap_obj soap_obj
end end
MapKeyName = XSD::QName.new(nil, "key") MapKeyName = XSD::QName.new(nil, "key")
MapValueName = XSD::QName.new(nil, "value") MapValueName = XSD::QName.new(nil, "value")
def map2soap(obj, type_qname, type) def map2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
keytype = type.child_type(MapKeyName) || XSD::AnyTypeName keytype = type.child_type(MapKeyName) || XSD::AnyTypeName
valuetype = type.child_type(MapValueName) || XSD::AnyTypeName valuetype = type.child_type(MapValueName) || XSD::AnyTypeName
soap_obj = SOAPStruct.new(MapQName) soap_obj = SOAPStruct.new(MapQName)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj) mark_marshalled_obj(obj, soap_obj)
obj.each do |key, value| obj.each do |key, value|
elem = SOAPStruct.new elem = SOAPStruct.new
@ -156,16 +197,71 @@ private
# ApacheAxis allows only 'item' here. # ApacheAxis allows only 'item' here.
soap_obj.add("item", elem) soap_obj.add("item", elem)
end end
end
soap_obj soap_obj
end end
def elements2soap(obj, soap_obj, elements) def elements2soap(obj, soap_obj, elements)
elements.each do |element| elements.each do |element|
name = element.name.name name = element.name.name
child_obj = obj.instance_variable_get('@' + name) child_obj = Mapping.get_attribute(obj, name)
soap_obj.add(name, Mapping._obj2soap(child_obj, self, element.type)) soap_obj.add(name,
Mapping._obj2soap(child_obj, self, element.type || element.name))
end end
end end
def any2obj(node, obj_class)
unless obj_class
typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name)
obj_class = Mapping.class_from_name(typestr)
end
if obj_class and obj_class.class_variables.include?('@@schema_element')
soap2stubobj(node, obj_class)
else
Mapping._soap2obj(node, Mapping::DefaultRegistry, obj_class)
end
end
def soap2stubobj(node, obj_class)
obj = Mapping.create_empty_object(obj_class)
unless node.is_a?(SOAPNil)
add_elements2stubobj(node, obj)
end
obj
end
def add_elements2stubobj(node, obj)
elements, as_array = schema_element_definition(obj.class)
vars = {}
node.each do |name, value|
if class_name = elements[name]
if klass = Mapping.class_from_name(class_name)
# klass must be a SOAPBasetype or a class
if klass.ancestors.include?(::SOAP::SOAPBasetype)
if value.respond_to?(:data)
child = klass.new(value.data).data
else
child = klass.new(nil).data
end
else
child = Mapping._soap2obj(value, self, klass)
end
else
raise MappingError.new("unknown class: #{class_name}")
end
else # untyped element is treated as anyType.
child = Mapping._soap2obj(value, self)
end
vars[name] = child
end
Mapping.set_attributes(obj, vars)
end
# it caches @@schema_element. this means that @@schema_element must not be
# changed while a lifetime of a WSDLLiteralRegistry.
def schema_element_definition(klass)
@schema_element_cache[klass] ||= Mapping.schema_element_definition(klass)
end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - WSDL literal mapping registry. # SOAP4R - WSDL literal mapping registry.
# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -10,48 +10,55 @@ require 'soap/baseData'
require 'soap/mapping/mapping' require 'soap/mapping/mapping'
require 'soap/mapping/typeMap' require 'soap/mapping/typeMap'
require 'xsd/codegen/gensupport' require 'xsd/codegen/gensupport'
require 'xsd/namedelements'
module SOAP module SOAP
module Mapping module Mapping
class WSDLLiteralRegistry class WSDLLiteralRegistry < Registry
attr_reader :definedelements attr_reader :definedelements
attr_reader :definedtypes attr_reader :definedtypes
attr_accessor :excn_handler_obj2soap attr_accessor :excn_handler_obj2soap
attr_accessor :excn_handler_soap2obj attr_accessor :excn_handler_soap2obj
def initialize(definedelements = nil, definedtypes = nil) def initialize(definedtypes = XSD::NamedElements::Empty,
@definedelements = definedelements definedelements = XSD::NamedElements::Empty)
@definedtypes = definedtypes @definedtypes = definedtypes
@rubytype_factory = RubytypeFactory.new(:allow_original_mapping => false) @definedelements = definedelements
@excn_handler_obj2soap = nil
@excn_handler_soap2obj = nil
@schema_element_cache = {} @schema_element_cache = {}
@schema_attribute_cache = {}
end end
def obj2soap(obj, qname) def obj2soap(obj, qname)
ret = nil soap_obj = nil
if !@definedelements.nil? && ele = @definedelements[qname] if ele = @definedelements[qname]
ret = _obj2soap(obj, ele) soap_obj = obj2elesoap(obj, ele)
elsif !@definedtypes.nil? && type = @definedtypes[qname] elsif type = @definedtypes[qname]
ret = obj2type(obj, type) soap_obj = obj2typesoap(obj, type)
else else
ret = unknownobj2soap(obj, qname) soap_obj = any2soap(obj, qname)
end end
return ret if ret return soap_obj if soap_obj
if @excn_handler_obj2soap if @excn_handler_obj2soap
ret = @excn_handler_obj2soap.call(obj) { |yield_obj| soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
Mapping._obj2soap(yield_obj, self) Mapping._obj2soap(yield_obj, self)
} }
return ret if ret return soap_obj if soap_obj
end end
raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
end end
# node should be a SOAPElement # node should be a SOAPElement
def soap2obj(node) def soap2obj(node, obj_class = nil)
unless obj_class.nil?
raise MappingError.new("must not reach here")
end
begin begin
return soapele2obj(node) return any2obj(node)
rescue MappingError rescue MappingError
end end
if @excn_handler_soap2obj if @excn_handler_soap2obj
@ -62,84 +69,136 @@ class WSDLLiteralRegistry
rescue Exception rescue Exception
end end
end end
raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.") raise MappingError.new("cannot map #{node.type.name} to Ruby object")
end end
private private
def _obj2soap(obj, ele) def obj2elesoap(obj, ele)
o = nil o = nil
if ele.type if ele.type
if type = @definedtypes[ele.type] if type = @definedtypes[ele.type]
o = obj2type(obj, type) o = obj2typesoap(obj, type)
elsif type = TypeMap[ele.type] elsif type = TypeMap[ele.type]
o = base2soap(obj, type) o = base2soap(obj, type)
else else
raise MappingError.new("Cannot find type #{ele.type}.") raise MappingError.new("cannot find type #{ele.type}")
end end
o.elename = ele.name o.elename = ele.name
elsif ele.local_complextype elsif ele.local_complextype
o = SOAPElement.new(ele.name) o = obj2typesoap(obj, ele.local_complextype)
ele.local_complextype.each_element do |child_ele| o.elename = ele.name
o.add(_obj2soap(Mapping.find_attribute(obj, child_ele.name.name), add_attributes2soap(obj, o)
child_ele)) elsif ele.local_simpletype
end o = obj2typesoap(obj, ele.local_simpletype)
o.elename = ele.name
else else
raise MappingError.new('Illegal schema?') raise MappingError.new('illegal schema?')
end end
o o
end end
def obj2type(obj, type) def obj2typesoap(obj, type)
if type.is_a?(::WSDL::XMLSchema::SimpleType) if type.is_a?(::WSDL::XMLSchema::SimpleType)
simple2soap(obj, type) simpleobj2soap(obj, type)
else else
complex2soap(obj, type) complexobj2soap(obj, type)
end end
end end
def simple2soap(obj, type) def simpleobj2soap(obj, type)
o = base2soap(obj, TypeMap[type.base])
if type.restriction.enumeration.empty?
STDERR.puts(
"#{type.name}: simpleType which is not enum type not supported.")
return o
end
type.check_lexical_format(obj) type.check_lexical_format(obj)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
o = base2soap(obj, TypeMap[type.base])
o o
end end
def complex2soap(obj, type) def complexobj2soap(obj, type)
o = SOAPElement.new(type.name) o = SOAPElement.new(type.name)
type.each_element do |child_ele| type.each_element do |child_ele|
o.add(_obj2soap(Mapping.find_attribute(obj, child_ele.name.name), child = Mapping.get_attribute(obj, child_ele.name.name)
child_ele)) if child.nil?
if child_ele.nillable
# ToDo: test
# add empty element
o.add(obj2elesoap(nil))
elsif Integer(child_ele.minoccurs) == 0
# nothing to do
else
raise MappingError.new("nil not allowed: #{child_ele.name.name}")
end
elsif child_ele.map_as_array?
child.each do |item|
o.add(obj2elesoap(item, child_ele))
end
else
o.add(obj2elesoap(child, child_ele))
end
end end
o o
end end
def unknownobj2soap(obj, name) def any2soap(obj, qname)
if obj.class.class_variables.include?('@@schema_element') if obj.is_a?(SOAPElement)
ele = SOAPElement.new(name) obj
elsif obj.class.class_variables.include?('@@schema_element')
stubobj2soap(obj, qname)
elsif obj.is_a?(SOAP::Mapping::Object)
mappingobj2soap(obj, qname)
elsif obj.is_a?(Hash)
ele = SOAPElement.from_obj(obj)
ele.elename = qname
ele
else
# expected to be a basetype or an anyType.
# SOAPStruct, etc. is used instead of SOAPElement.
begin
ele = Mapping.obj2soap(obj)
ele.elename = qname
ele
rescue MappingError
ele = SOAPElement.new(qname, obj.to_s)
end
if obj.respond_to?(:__xmlattr)
obj.__xmlattr.each do |key, value|
ele.extraattr[key] = value
end
end
ele
end
end
def stubobj2soap(obj, qname)
ele = SOAPElement.new(qname)
add_elements2soap(obj, ele) add_elements2soap(obj, ele)
add_attributes2soap(obj, ele) add_attributes2soap(obj, ele)
ele ele
elsif obj.is_a?(Hash)
ele = SOAPElement.from_obj(obj)
ele.elename = name
ele
else # expected to be a basetype or an anyType.
o = Mapping.obj2soap(obj)
o.elename = name
o
end end
def mappingobj2soap(obj, qname)
ele = SOAPElement.new(qname)
obj.__xmlele.each do |key, value|
if value.is_a?(::Array)
value.each do |item|
ele.add(obj2soap(item, key))
end
else
ele.add(obj2soap(value, key))
end
end
obj.__xmlattr.each do |key, value|
ele.extraattr[key] = value
end
ele
end end
def add_elements2soap(obj, ele) def add_elements2soap(obj, ele)
elements, as_array = schema_element_definition(obj.class) elements, as_array = schema_element_definition(obj.class)
if elements
elements.each do |elename, type| elements.each do |elename, type|
child = Mapping.find_attribute(obj, elename) child = Mapping.get_attribute(obj, elename)
name = ::XSD::QName.new(nil, elename) unless child.nil?
name = XSD::QName.new(nil, elename)
if as_array.include?(type) if as_array.include?(type)
child.each do |item| child.each do |item|
ele.add(obj2soap(item, name)) ele.add(obj2soap(item, name))
@ -149,21 +208,25 @@ private
end end
end end
end end
end
end
def add_attributes2soap(obj, ele) def add_attributes2soap(obj, ele)
attributes = schema_attribute_definition(obj.class) attributes = schema_attribute_definition(obj.class)
attributes.each do |attrname, param| if attributes
attr = Mapping.find_attribute(obj, 'attr_' + attrname) attributes.each do |qname, param|
ele.extraattr[attrname] = attr attr = obj.__send__('xmlattr_' +
XSD::CodeGen::GenSupport.safevarname(qname.name))
ele.extraattr[qname] = attr
end
end end
end end
def base2soap(obj, type) def base2soap(obj, type)
soap_obj = nil soap_obj = nil
if type <= ::XSD::XSDString if type <= XSD::XSDString
soap_obj = type.new(::XSD::Charset.is_ces(obj, $KCODE) ? soap_obj = type.new(XSD::Charset.is_ces(obj, $KCODE) ?
::XSD::Charset.encoding_conv(obj, $KCODE, ::XSD::Charset.encoding) : XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) : obj)
obj)
else else
soap_obj = type.new(obj) soap_obj = type.new(obj)
end end
@ -176,35 +239,41 @@ private
end end
klass = ::SOAP::Mapping::Object klass = ::SOAP::Mapping::Object
obj = klass.new obj = klass.new
node.each do |name, value|
obj.__soap_set_property(name, Mapping.soap2obj(value))
end
obj obj
end end
def soapele2obj(node, obj_class = nil) def any2obj(node, obj_class = nil)
unless obj_class unless obj_class
typestr = ::XSD::CodeGen::GenSupport.safeconstname(node.elename.name) typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name)
obj_class = Mapping.class_from_name(typestr) obj_class = Mapping.class_from_name(typestr)
end end
if obj_class and obj_class.class_variables.include?('@@schema_element') if obj_class and obj_class.class_variables.include?('@@schema_element')
soapele2definedobj(node, obj_class) soapele2stubobj(node, obj_class)
elsif node.is_a?(SOAPElement) elsif node.is_a?(SOAPElement) or node.is_a?(SOAPStruct)
node.to_obj # SOAPArray for literal?
soapele2plainobj(node)
else else
result, obj = @rubytype_factory.soap2obj(nil, node, nil, self) obj = Mapping._soap2obj(node, Mapping::DefaultRegistry, obj_class)
add_attributes2plainobj(node, obj)
obj obj
end end
end end
def soapele2definedobj(node, obj_class) def soapele2stubobj(node, obj_class)
obj = Mapping.create_empty_object(obj_class) obj = Mapping.create_empty_object(obj_class)
add_elements2obj(node, obj) add_elements2stubobj(node, obj)
add_attributes2obj(node, obj) add_attributes2stubobj(node, obj)
obj obj
end end
def add_elements2obj(node, obj) def soapele2plainobj(node)
obj = anytype2obj(node)
add_elements2plainobj(node, obj)
add_attributes2plainobj(node, obj)
obj
end
def add_elements2stubobj(node, obj)
elements, as_array = schema_element_definition(obj.class) elements, as_array = schema_element_definition(obj.class)
vars = {} vars = {}
node.each do |name, value| node.each do |name, value|
@ -217,13 +286,13 @@ private
child = klass.new(nil).data child = klass.new(nil).data
end end
else else
child = soapele2obj(value, klass) child = any2obj(value, klass)
end end
else else
raise MappingError.new("Unknown class: #{class_name}") raise MappingError.new("unknown class: #{class_name}")
end end
else # untyped element is treated as anyType. else # untyped element is treated as anyType.
child = anytype2obj(value) child = any2obj(value)
end end
if as_array.include?(class_name) if as_array.include?(class_name)
(vars[name] ||= []) << child (vars[name] ||= []) << child
@ -231,15 +300,14 @@ private
vars[name] = child vars[name] = child
end end
end end
Mapping.set_instance_vars(obj, vars) Mapping.set_attributes(obj, vars)
end end
def add_attributes2obj(node, obj) def add_attributes2stubobj(node, obj)
Mapping.set_instance_vars(obj, {'__soap_attribute' => {}}) if attributes = schema_attribute_definition(obj.class)
vars = {} define_xmlattr(obj)
attributes = schema_attribute_definition(obj.class) attributes.each do |qname, class_name|
attributes.each do |attrname, class_name| attr = node.extraattr[qname]
attr = node.extraattr[::XSD::QName.new(nil, attrname)]
next if attr.nil? or attr.empty? next if attr.nil? or attr.empty?
klass = Mapping.class_from_name(class_name) klass = Mapping.class_from_name(class_name)
if klass.ancestors.include?(::SOAP::SOAPBasetype) if klass.ancestors.include?(::SOAP::SOAPBasetype)
@ -247,32 +315,77 @@ private
else else
child = attr child = attr
end end
vars['attr_' + attrname] = child obj.__xmlattr[qname] = child
define_xmlattr_accessor(obj, qname)
end
end
end
def add_elements2plainobj(node, obj)
node.each do |name, value|
obj.__add_xmlele_value(XSD::QName.new(nil, name), any2obj(value))
end
end
def add_attributes2plainobj(node, obj)
return if node.extraattr.empty?
define_xmlattr(obj)
node.extraattr.each do |qname, value|
obj.__xmlattr[qname] = value
define_xmlattr_accessor(obj, qname)
end
end
if RUBY_VERSION > "1.7.0"
def define_xmlattr_accessor(obj, qname)
name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
Mapping.define_attr_accessor(obj, 'xmlattr_' + name,
proc { @__xmlattr[qname] },
proc { |value| @__xmlattr[qname] = value })
end
else
def define_xmlattr_accessor(obj, qname)
name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
obj.instance_eval <<-EOS
def #{name}
@__xmlattr[#{qname.dump}]
end
def #{name}=(value)
@__xmlattr[#{qname.dump}] = value
end
EOS
end
end
if RUBY_VERSION > "1.7.0"
def define_xmlattr(obj)
obj.instance_variable_set('@__xmlattr', {})
unless obj.respond_to?(:__xmlattr)
Mapping.define_attr_accessor(obj, :__xmlattr, proc { @__xmlattr })
end
end
else
def define_xmlattr(obj)
obj.instance_variable_set('@__xmlattr', {})
unless obj.respond_to?(:__xmlattr)
obj.instance_eval <<-EOS
def __xmlattr
@__xmlattr
end
EOS
end
end end
Mapping.set_instance_vars(obj, vars)
end end
# it caches @@schema_element. this means that @@schema_element must not be # it caches @@schema_element. this means that @@schema_element must not be
# changed while a lifetime of a WSDLLiteralRegistry. # changed while a lifetime of a WSDLLiteralRegistry.
def schema_element_definition(klass) def schema_element_definition(klass)
if @schema_element_cache.key?(klass) @schema_element_cache[klass] ||= Mapping.schema_element_definition(klass)
return @schema_element_cache[klass]
end
elements = {}
as_array = []
klass.class_eval('@@schema_element').each do |name, class_name|
if /\[\]$/ =~ class_name
class_name = class_name.sub(/\[\]$/, '')
as_array << class_name
end
elements[name] = class_name
end
@schema_element_cache[klass] = [elements, as_array]
return @schema_element_cache[klass]
end end
def schema_attribute_definition(klass) def schema_attribute_definition(klass)
attributes = klass.class_eval('@@schema_attribute') @schema_attribute_cache[klass] ||= Mapping.schema_attribute_definition(klass)
end end
end end

View File

@ -14,8 +14,9 @@ module SOAP
module Marshal module Marshal
# Trying xsd:dateTime data to be recovered as aTime. aDateTime if it fails. # Trying xsd:dateTime data to be recovered as aTime.
MarshalMappingRegistry = Mapping::Registry.new(:allow_original_mapping => true) MarshalMappingRegistry = Mapping::Registry.new(
:allow_original_mapping => true)
MarshalMappingRegistry.add( MarshalMappingRegistry.add(
Time, Time,
::SOAP::SOAPDateTime, ::SOAP::SOAPDateTime,

View File

@ -43,18 +43,23 @@ class NetHttpClient
raise NotImplementedError.new("not supported for now") raise NotImplementedError.new("not supported for now")
end end
def proxy=(proxy_str) def proxy=(proxy)
if proxy_str.nil? if proxy.nil?
@proxy = nil @proxy = nil
else else
@proxy = URI.parse(proxy_str) if proxy.is_a?(URI)
@proxy = proxy
else
@proxy = URI.parse(proxy)
end
if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or
@proxy.host == nil or @proxy.port == nil @proxy.host == nil or @proxy.port == nil
raise ArgumentError.new("unsupported proxy `#{proxy_str}'") raise ArgumentError.new("unsupported proxy `#{proxy}'")
end end
end
reset_all
@proxy @proxy
end end
end
def set_basic_auth(uri, user_id, passwd) def set_basic_auth(uri, user_id, passwd)
# net/http does not handle url. # net/http does not handle url.
@ -79,7 +84,9 @@ class NetHttpClient
end end
def post(url, req_body, header = {}) def post(url, req_body, header = {})
unless url.is_a?(URI)
url = URI.parse(url) url = URI.parse(url)
end
extra = header.dup extra = header.dup
extra['User-Agent'] = @agent if @agent extra['User-Agent'] = @agent if @agent
res = start(url) { |http| res = start(url) { |http|
@ -89,7 +96,9 @@ class NetHttpClient
end end
def get_content(url, header = {}) def get_content(url, header = {})
unless url.is_a?(URI)
url = URI.parse(url) url = URI.parse(url)
end
extra = header.dup extra = header.dup
extra['User-Agent'] = @agent if @agent extra['User-Agent'] = @agent if @agent
res = start(url) { |http| res = start(url) { |http|

View File

@ -1,5 +1,5 @@
# SOAP4R - CGI stub library # SOAP4R - CGI/mod_ruby stub library
# Copyright (C) 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -10,7 +10,7 @@ require 'soap/streamHandler'
require 'webrick/httpresponse' require 'webrick/httpresponse'
require 'webrick/httpstatus' require 'webrick/httpstatus'
require 'logger' require 'logger'
require 'soap/rpc/router' require 'soap/rpc/soaplet'
module SOAP module SOAP
@ -26,57 +26,51 @@ module RPC
# #
class CGIStub < Logger::Application class CGIStub < Logger::Application
include SOAP include SOAP
include WEBrick
# There is a client which does not accept the media-type which is defined in
# SOAP spec.
attr_accessor :mediatype
class CGIError < Error; end
class SOAPRequest class SOAPRequest
ALLOWED_LENGTH = 1024 * 1024 attr_reader :body
def initialize(stream = $stdin) def [](var); end
@method = ENV['REQUEST_METHOD']
@size = ENV['CONTENT_LENGTH'].to_i || 0 def meta_vars; end
@contenttype = ENV['CONTENT_TYPE']
@soapaction = ENV['HTTP_SOAPAction']
@source = stream
@body = nil
end end
def init class SOAPStdinRequest < SOAPRequest
validate attr_reader :body
@body = @source.read(@size)
self def initialize(stream)
size = ENV['CONTENT_LENGTH'].to_i || 0
@body = stream.read(size)
end end
def dump def [](var)
@body.dup ENV[var.gsub(/-/, '_').upcase]
end end
def soapaction def meta_vars
@soapaction {
'HTTP_SOAPACTION' => ENV['HTTP_SOAPAction']
}
end
end end
def contenttype class SOAPFCGIRequest < SOAPRequest
@contenttype attr_reader :body
def initialize(request)
@request = request
@body = @request.in.read
end end
def to_s def [](var)
"method: #{ @method }, size: #{ @size }" @request.env[var.gsub(/-/, '_').upcase]
end end
private def meta_vars
{
def validate # raise CGIError 'HTTP_SOAPACTION' => @request.env['HTTP_SOAPAction']
if @method != 'POST' }
raise CGIError.new("Method '#{ @method }' not allowed.")
end
if @size > ALLOWED_LENGTH
raise CGIError.new("Content-length too long.")
end
end end
end end
@ -85,33 +79,14 @@ class CGIStub < Logger::Application
set_log(STDERR) set_log(STDERR)
self.level = ERROR self.level = ERROR
@default_namespace = default_namespace @default_namespace = default_namespace
@router = SOAP::RPC::Router.new(appname)
@remote_user = ENV['REMOTE_USER'] || 'anonymous'
@remote_host = ENV['REMOTE_HOST'] || ENV['REMOTE_ADDR'] || 'unknown' @remote_host = ENV['REMOTE_HOST'] || ENV['REMOTE_ADDR'] || 'unknown'
@request = nil @router = ::SOAP::RPC::Router.new(self.class.name)
@response = nil @soaplet = ::SOAP::RPC::SOAPlet.new(@router)
@mediatype = MediaType
on_init on_init
end end
def add_rpc_servant(obj, namespace = @default_namespace, soapaction = nil)
RPC.defined_methods(obj).each do |name|
qname = XSD::QName.new(namespace, name)
param_size = obj.method(name).arity.abs
params = (1..param_size).collect { |i| "p#{i}" }
param_def = SOAP::RPC::SOAPMethod.create_param_def(params)
@router.add_method(obj, qname, soapaction, name, param_def)
end
end
alias add_servant add_rpc_servant
def add_rpc_headerhandler(obj)
@router.headerhandler << obj
end
alias add_headerhandler add_rpc_headerhandler
def on_init def on_init
# Override this method in derived class to call 'add_method' to add methods. # do extra initialization in a derived class if needed.
end end
def mapping_registry def mapping_registry
@ -122,83 +97,108 @@ class CGIStub < Logger::Application
@router.mapping_registry = value @router.mapping_registry = value
end end
def add_method(receiver, name, *param) def generate_explicit_type
add_method_with_namespace_as(@default_namespace, receiver, @router.generate_explicit_type
name, name, *param)
end end
def add_method_as(receiver, name, name_as, *param) def generate_explicit_type=(generate_explicit_type)
add_method_with_namespace_as(@default_namespace, receiver, @router.generate_explicit_type = generate_explicit_type
name, name_as, *param)
end end
def add_method_with_namespace(namespace, receiver, name, *param) # servant entry interface
add_method_with_namespace_as(namespace, receiver, name, name, *param)
end
def add_method_with_namespace_as(namespace, receiver, name, name_as, *param) def add_rpc_servant(obj, namespace = @default_namespace)
param_def = if param.size == 1 and param[0].is_a?(Array) @router.add_rpc_servant(obj, namespace)
param[0]
else
SOAP::RPC::SOAPMethod.create_param_def(param)
end end
alias add_servant add_rpc_servant
def add_headerhandler(obj)
@router.add_headerhandler(obj)
end
alias add_rpc_headerhandler add_headerhandler
# method entry interface
def add_rpc_method(obj, name, *param)
add_rpc_method_with_namespace_as(@default_namespace, obj, name, name, *param)
end
alias add_method add_rpc_method
def add_rpc_method_as(obj, name, name_as, *param)
add_rpc_method_with_namespace_as(@default_namespace, obj, name, name_as, *param)
end
alias add_method_as add_rpc_method_as
def add_rpc_method_with_namespace(namespace, obj, name, *param)
add_rpc_method_with_namespace_as(namespace, obj, name, name, *param)
end
alias add_method_with_namespace add_rpc_method_with_namespace
def add_rpc_method_with_namespace_as(namespace, obj, name, name_as, *param)
qname = XSD::QName.new(namespace, name_as) qname = XSD::QName.new(namespace, name_as)
@router.add_method(receiver, qname, nil, name, param_def) soapaction = nil
param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
@router.add_rpc_operation(obj, qname, soapaction, name, param_def)
end
alias add_method_with_namespace_as add_rpc_method_with_namespace_as
def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
@router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
end end
def route(conn_data) def add_document_operation(receiver, soapaction, name, param_def, opt = {})
@router.route(conn_data) @router.add_document_operation(receiver, soapaction, name, param_def, opt)
end end
def create_fault_response(e) def set_fcgi_request(request)
@router.create_fault_response(e) @fcgi = request
end end
private private
HTTPVersion = WEBrick::HTTPVersion.new('1.0') # dummy; ignored
def run def run
prologue res = WEBrick::HTTPResponse.new({:HTTPVersion => HTTPVersion})
httpversion = WEBrick::HTTPVersion.new('1.0')
@response = WEBrick::HTTPResponse.new({:HTTPVersion => httpversion})
conn_data = nil
begin begin
@log.info { "Received a request from '#{ @remote_user }@#{ @remote_host }'." } @log.info { "received a request from '#{ @remote_host }'" }
# SOAP request parsing. if @fcgi
@request = SOAPRequest.new.init req = SOAPFCGIRequest.new(@fcgi)
@response['Status'] = 200 else
conn_data = ::SOAP::StreamHandler::ConnectionData.new req = SOAPStdinRequest.new($stdin)
conn_data.receive_string = @request.dump
conn_data.receive_contenttype = @request.contenttype
@log.debug { "XML Request: #{conn_data.receive_string}" }
conn_data = route(conn_data)
@log.debug { "XML Response: #{conn_data.send_string}" }
if conn_data.is_fault
@response['Status'] = 500
end end
@response['Cache-Control'] = 'private' @soaplet.do_POST(req, res)
@response.body = conn_data.send_string rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
@response['content-type'] = conn_data.send_contenttype res.set_error(ex)
rescue Exception rescue HTTPStatus::Error => ex
conn_data = create_fault_response($!) res.set_error(ex)
@response['Cache-Control'] = 'private' rescue HTTPStatus::Status => ex
@response['Status'] = 500 res.status = ex.code
@response.body = conn_data.send_string rescue StandardError, NameError => ex # for Ruby 1.6
@response['content-type'] = conn_data.send_contenttype || @mediatype res.set_error(ex, true)
ensure ensure
if defined?(MOD_RUBY)
r = Apache.request
r.status = res.status
r.content_type = res.content_type
r.send_http_header
buf = res.body
else
buf = '' buf = ''
@response.send_response(buf) res.send_response(buf)
buf.sub!(/^[^\r]+\r\n/, '') # Trim status line. buf.sub!(/^[^\r]+\r\n/, '') # Trim status line.
@log.debug { "SOAP CGI Response:\n#{ buf }" }
print buf
epilogue
end end
@log.debug { "SOAP CGI Response:\n#{ buf }" }
if @fcgi
@fcgi.out.print buf
@fcgi.finish
@fcgi = nil
else
print buf
end
end
0 0
end end
def prologue; end
def epilogue; end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - SOAP RPC driver # SOAP4R - SOAP RPC driver
# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -22,32 +22,55 @@ module RPC
class Driver class Driver
class EmptyResponseError < Error; end
class << self class << self
if RUBY_VERSION >= "1.7.0"
def __attr_proxy(symbol, assignable = false) def __attr_proxy(symbol, assignable = false)
name = symbol.to_s name = symbol.to_s
self.__send__(:define_method, name, proc { self.__send__(:define_method, name, proc {
@servant.__send__(name) @proxy.__send__(name)
}) })
if assignable if assignable
self.__send__(:define_method, name + '=', proc { |rhs| self.__send__(:define_method, name + '=', proc { |rhs|
@servant.__send__(name + '=', rhs) @proxy.__send__(name + '=', rhs)
}) })
end end
end end
else
def __attr_proxy(symbol, assignable = false)
name = symbol.to_s
module_eval <<-EOS
def #{name}
@proxy.#{name}
end
EOS
if assignable
module_eval <<-EOS
def #{name}=(value)
@proxy.#{name} = value
end
EOS
end
end
end
end end
__attr_proxy :options
__attr_proxy :headerhandler
__attr_proxy :streamhandler
__attr_proxy :test_loopback_response
__attr_proxy :endpoint_url, true __attr_proxy :endpoint_url, true
__attr_proxy :mapping_registry, true __attr_proxy :mapping_registry, true
__attr_proxy :soapaction, true
__attr_proxy :default_encodingstyle, true __attr_proxy :default_encodingstyle, true
__attr_proxy :generate_explicit_type, true __attr_proxy :generate_explicit_type, true
__attr_proxy :allow_unqualified_element, true __attr_proxy :allow_unqualified_element, true
__attr_proxy :headerhandler
__attr_proxy :streamhandler
__attr_proxy :test_loopback_response
__attr_proxy :reset_stream
attr_reader :proxy
attr_reader :options
attr_accessor :soapaction
def inspect
"#<#{self.class}:#{@proxy.inspect}>"
end
def httpproxy def httpproxy
options["protocol.http.proxy"] options["protocol.http.proxy"]
@ -81,10 +104,12 @@ class Driver
options["protocol.wiredump_file_base"] = wiredump_file_base options["protocol.wiredump_file_base"] = wiredump_file_base
end end
def initialize(endpoint_url, namespace, soapaction = nil) def initialize(endpoint_url, namespace = nil, soapaction = nil)
@servant = Servant__.new(self, endpoint_url, namespace) @namespace = namespace
@servant.soapaction = soapaction @soapaction = soapaction
@proxy = @servant.proxy @options = setup_options
@wiredump_file_base = nil
@proxy = Proxy.new(endpoint_url, @soapaction, @options)
end end
def loadproperty(propertyname) def loadproperty(propertyname)
@ -93,28 +118,23 @@ class Driver
end end
end end
def inspect
"#<#{self.class}:#{@servant.inspect}>"
end
def add_rpc_method(name, *params) def add_rpc_method(name, *params)
param_def = create_rpc_param_def(params) add_rpc_method_with_soapaction_as(name, name, @soapaction, *params)
@servant.add_rpc_method(name, @servant.soapaction, name, param_def)
end end
def add_rpc_method_as(name, name_as, *params) def add_rpc_method_as(name, name_as, *params)
param_def = create_rpc_param_def(params) add_rpc_method_with_soapaction_as(name, name_as, @soapaction, *params)
@servant.add_rpc_method(name_as, @servant.soapaction, name, param_def)
end end
def add_rpc_method_with_soapaction(name, soapaction, *params) def add_rpc_method_with_soapaction(name, soapaction, *params)
param_def = create_rpc_param_def(params) add_rpc_method_with_soapaction_as(name, name, soapaction, *params)
@servant.add_rpc_method(name, soapaction, name, param_def)
end end
def add_rpc_method_with_soapaction_as(name, name_as, soapaction, *params) def add_rpc_method_with_soapaction_as(name, name_as, soapaction, *params)
param_def = create_rpc_param_def(params) param_def = SOAPMethod.create_rpc_param_def(params)
@servant.add_rpc_method(name_as, soapaction, name, param_def) qname = XSD::QName.new(@namespace, name_as)
@proxy.add_rpc_method(qname, soapaction, name, param_def)
add_rpc_method_interface(name, param_def)
end end
# add_method is for shortcut of typical rpc/encoded method definition. # add_method is for shortcut of typical rpc/encoded method definition.
@ -123,138 +143,20 @@ class Driver
alias add_method_with_soapaction add_rpc_method_with_soapaction alias add_method_with_soapaction add_rpc_method_with_soapaction
alias add_method_with_soapaction_as add_rpc_method_with_soapaction_as alias add_method_with_soapaction_as add_rpc_method_with_soapaction_as
def add_document_method(name, req_qname, res_qname) def add_document_method(name, soapaction, req_qname, res_qname)
param_def = create_document_param_def(name, req_qname, res_qname) param_def = SOAPMethod.create_doc_param_def(req_qname, res_qname)
@servant.add_document_method(name, @servant.soapaction, name, param_def) @proxy.add_document_method(soapaction, name, param_def)
add_document_method_interface(name, param_def)
end end
def add_document_method_as(name, name_as, req_qname, res_qname) def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
param_def = create_document_param_def(name, req_qname, res_qname) @proxy.add_rpc_operation(qname, soapaction, name, param_def, opt)
@servant.add_document_method(name_as, @servant.soapaction, name, param_def) add_rpc_method_interface(name, param_def)
end end
def add_document_method_with_soapaction(name, soapaction, req_qname, def add_document_operation(soapaction, name, param_def, opt = {})
res_qname) @proxy.add_document_operation(soapaction, name, param_def, opt)
param_def = create_document_param_def(name, req_qname, res_qname) add_document_method_interface(name, param_def)
@servant.add_document_method(name, soapaction, name, param_def)
end
def add_document_method_with_soapaction_as(name, name_as, soapaction,
req_qname, res_qname)
param_def = create_document_param_def(name, req_qname, res_qname)
@servant.add_document_method(name_as, soapaction, name, param_def)
end
def reset_stream
@servant.reset_stream
end
def invoke(headers, body)
@servant.invoke(headers, body)
end
def call(name, *params)
@servant.call(name, *params)
end
private
def create_rpc_param_def(params)
if params.size == 1 and params[0].is_a?(Array)
params[0]
else
SOAPMethod.create_param_def(params)
end
end
def create_document_param_def(name, req_qname, res_qname)
[
['input', name, [nil, req_qname.namespace, req_qname.name]],
['output', name, [nil, res_qname.namespace, res_qname.name]]
]
end
def add_rpc_method_interface(name, param_def)
@servant.add_rpc_method_interface(name, param_def)
end
def add_document_method_interface(name, paramname)
@servant.add_document_method_interface(name, paramname)
end
class Servant__
attr_reader :proxy
attr_reader :options
attr_accessor :soapaction
def initialize(host, endpoint_url, namespace)
@host = host
@namespace = namespace
@soapaction = nil
@options = setup_options
@wiredump_file_base = nil
@endpoint_url = endpoint_url
@proxy = Proxy.new(endpoint_url, @soapaction, @options)
end
def inspect
"#<#{self.class}:#{@proxy.inspect}>"
end
def endpoint_url
@proxy.endpoint_url
end
def endpoint_url=(endpoint_url)
@proxy.endpoint_url = endpoint_url
end
def mapping_registry
@proxy.mapping_registry
end
def mapping_registry=(mapping_registry)
@proxy.mapping_registry = mapping_registry
end
def default_encodingstyle
@proxy.default_encodingstyle
end
def default_encodingstyle=(encodingstyle)
@proxy.default_encodingstyle = encodingstyle
end
def generate_explicit_type
@proxy.generate_explicit_type
end
def generate_explicit_type=(generate_explicit_type)
@proxy.generate_explicit_type = generate_explicit_type
end
def allow_unqualified_element
@proxy.allow_unqualified_element
end
def allow_unqualified_element=(allow_unqualified_element)
@proxy.allow_unqualified_element = allow_unqualified_element
end
def headerhandler
@proxy.headerhandler
end
def streamhandler
@proxy.streamhandler
end
def test_loopback_response
@proxy.test_loopback_response
end
def reset_stream
@proxy.reset_stream
end end
def invoke(headers, body) def invoke(headers, body)
@ -275,48 +177,11 @@ private
@proxy.call(name, *params) @proxy.call(name, *params)
end end
def add_rpc_method(name_as, soapaction, name, param_def) private
qname = XSD::QName.new(@namespace, name_as)
@proxy.add_rpc_method(qname, soapaction, name, param_def)
add_rpc_method_interface(name, param_def)
end
def add_document_method(name_as, soapaction, name, param_def)
qname = XSD::QName.new(@namespace, name_as)
@proxy.add_document_method(qname, soapaction, name, param_def)
add_document_method_interface(name, param_def)
end
def add_rpc_method_interface(name, param_def)
param_count = 0
@proxy.operation[name].each_param_name(RPC::SOAPMethod::IN,
RPC::SOAPMethod::INOUT) do |param_name|
param_count += 1
end
sclass = class << @host; self; end
sclass.__send__(:define_method, name, proc { |*arg|
unless arg.size == param_count
raise ArgumentError.new(
"wrong number of arguments (#{arg.size} for #{param_count})")
end
@servant.call(name, *arg)
})
@host.method(name)
end
def add_document_method_interface(name, paramname)
sclass = class << @host; self; end
sclass.__send__(:define_method, name, proc { |param|
@servant.call(name, param)
})
@host.method(name)
end
private
def set_wiredump_file_base(name) def set_wiredump_file_base(name)
if @wiredump_file_base if @wiredump_file_base
@proxy.set_wiredump_file_base(@wiredump_file_base + "_#{ name }") @proxy.set_wiredump_file_base("#{@wiredump_file_base}_#{name}")
end end
end end
@ -344,6 +209,42 @@ private
opt["protocol.http.no_proxy"] ||= Env::NO_PROXY opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
opt opt
end end
def add_rpc_method_interface(name, param_def)
param_count = RPC::SOAPMethod.param_count(param_def,
RPC::SOAPMethod::IN, RPC::SOAPMethod::INOUT)
add_method_interface(name, param_count)
end
def add_document_method_interface(name, param_def)
param_count = RPC::SOAPMethod.param_count(param_def, RPC::SOAPMethod::IN)
add_method_interface(name, param_count)
end
if RUBY_VERSION > "1.7.0"
def add_method_interface(name, param_count)
::SOAP::Mapping.define_singleton_method(self, name) do |*arg|
unless arg.size == param_count
raise ArgumentError.new(
"wrong number of arguments (#{arg.size} for #{param_count})")
end
call(name, *arg)
end
self.method(name)
end
else
def add_method_interface(name, param_count)
instance_eval <<-EOS
def #{name}(*arg)
unless arg.size == #{param_count}
raise ArgumentError.new(
"wrong number of arguments (\#{arg.size} for #{param_count})")
end
call(#{name.dump}, *arg)
end
EOS
self.method(name)
end
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - RPC element definition. # SOAP4R - RPC element definition.
# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000, 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -77,6 +77,8 @@ class SOAPMethod < SOAPStruct
attr_reader :param_def attr_reader :param_def
attr_reader :inparam attr_reader :inparam
attr_reader :outparam attr_reader :outparam
attr_reader :retval_name
attr_reader :retval_class_name
def initialize(qname, param_def = nil) def initialize(qname, param_def = nil)
super(nil) super(nil)
@ -93,6 +95,7 @@ class SOAPMethod < SOAPStruct
@inparam = {} @inparam = {}
@outparam = {} @outparam = {}
@retval_name = nil @retval_name = nil
@retval_class_name = nil
init_param(@param_def) if @param_def init_param(@param_def) if @param_def
end end
@ -101,12 +104,12 @@ class SOAPMethod < SOAPStruct
@outparam_names.size > 0 @outparam_names.size > 0
end end
def each_param_name(*type) def input_params
@signature.each do |io_type, name, param_type| collect_params(IN, INOUT)
if type.include?(io_type)
yield(name)
end
end end
def output_params
collect_params(OUT, INOUT)
end end
def set_param(params) def set_param(params)
@ -124,7 +127,30 @@ class SOAPMethod < SOAPStruct
end end
end end
def SOAPMethod.create_param_def(param_names) def SOAPMethod.param_count(param_def, *type)
count = 0
param_def.each do |io_type, name, param_type|
if type.include?(io_type)
count += 1
end
end
count
end
def SOAPMethod.derive_rpc_param_def(obj, name, *param)
if param.size == 1 and param[0].is_a?(Array)
return param[0]
end
if param.empty?
method = obj.method(name)
param_names = (1..method.arity.abs).collect { |i| "p#{i}" }
else
param_names = param
end
create_rpc_param_def(param_names)
end
def SOAPMethod.create_rpc_param_def(param_names)
param_def = [] param_def = []
param_names.each do |param_name| param_names.each do |param_name|
param_def.push([IN, param_name, nil]) param_def.push([IN, param_name, nil])
@ -133,8 +159,29 @@ class SOAPMethod < SOAPStruct
param_def param_def
end end
def SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
req_qnames = [req_qnames] if req_qnames.is_a?(XSD::QName)
res_qnames = [res_qnames] if res_qnames.is_a?(XSD::QName)
param_def = []
req_qnames.each do |qname|
param_def << [IN, qname.name, [nil, qname.namespace, qname.name]]
end
res_qnames.each do |qname|
param_def << [OUT, qname.name, [nil, qname.namespace, qname.name]]
end
param_def
end
private private
def collect_params(*type)
names = []
@signature.each do |io_type, name, param_type|
names << name if type.include?(io_type)
end
names
end
def init_param(param_def) def init_param(param_def)
param_def.each do |io_type, name, param_type| param_def.each do |io_type, name, param_type|
case io_type case io_type
@ -148,12 +195,20 @@ private
@signature.push([INOUT, name, param_type]) @signature.push([INOUT, name, param_type])
@inoutparam_names.push(name) @inoutparam_names.push(name)
when RETVAL when RETVAL
if (@retval_name) if @retval_name
raise MethodDefinitionError.new('Duplicated retval') raise MethodDefinitionError.new('duplicated retval')
end end
@retval_name = name @retval_name = name
@retval_class_name = nil
if param_type
if param_type[0].is_a?(String)
@retval_class_name = Mapping.class_from_name(param_type[0])
else else
raise MethodDefinitionError.new("Unknown type: #{ io_type }") @retval_class_name = param_type[0]
end
end
else
raise MethodDefinitionError.new("unknown type: #{io_type}")
end end
end end
end end
@ -168,7 +223,7 @@ class SOAPMethodRequest < SOAPMethod
param_value = [] param_value = []
i = 0 i = 0
params.each do |param| params.each do |param|
param_name = "p#{ i }" param_name = "p#{i}"
i += 1 i += 1
param_def << [IN, param_name, nil] param_def << [IN, param_name, nil]
param_value << [param_name, param] param_value << [param_name, param]
@ -186,9 +241,9 @@ class SOAPMethodRequest < SOAPMethod
end end
def each def each
each_param_name(IN, INOUT) do |name| input_params.each do |name|
unless @inparam[name] unless @inparam[name]
raise ParameterError.new("Parameter: #{ name } was not given.") raise ParameterError.new("parameter: #{name} was not given")
end end
yield(name, @inparam[name]) yield(name, @inparam[name])
end end
@ -200,10 +255,10 @@ class SOAPMethodRequest < SOAPMethod
req req
end end
def create_method_response def create_method_response(response_name = nil)
SOAPMethodResponse.new( response_name ||=
XSD::QName.new(@elename.namespace, @elename.name + 'Response'), XSD::QName.new(@elename.namespace, @elename.name + 'Response')
@param_def) SOAPMethodResponse.new(response_name, @param_def)
end end
private private
@ -211,7 +266,7 @@ private
def check_elename(qname) def check_elename(qname)
# NCName & ruby's method name # NCName & ruby's method name
unless /\A[\w_][\w\d_\-]*\z/ =~ qname.name unless /\A[\w_][\w\d_\-]*\z/ =~ qname.name
raise MethodDefinitionError.new("Element name '#{qname.name}' not allowed") raise MethodDefinitionError.new("element name '#{qname.name}' not allowed")
end end
end end
end end
@ -236,11 +291,11 @@ class SOAPMethodResponse < SOAPMethod
yield(@retval_name, @retval) yield(@retval_name, @retval)
end end
each_param_name(OUT, INOUT) do |param_name| output_params.each do |name|
unless @outparam[param_name] unless @outparam[name]
raise ParameterError.new("Parameter: #{ param_name } was not given.") raise ParameterError.new("parameter: #{name} was not given")
end end
yield(param_name, @outparam[param_name]) yield(name, @outparam[name])
end end
end end
end end

View File

@ -24,23 +24,22 @@ class HTTPServer < Logger::Application
super(config[:SOAPHTTPServerApplicationName] || self.class.name) super(config[:SOAPHTTPServerApplicationName] || self.class.name)
@default_namespace = config[:SOAPDefaultNamespace] @default_namespace = config[:SOAPDefaultNamespace]
@webrick_config = config.dup @webrick_config = config.dup
self.level = Logger::Severity::ERROR # keep silent by default
@webrick_config[:Logger] ||= @log @webrick_config[:Logger] ||= @log
@server = nil @log = @webrick_config[:Logger] # sync logger of App and HTTPServer
@soaplet = ::SOAP::RPC::SOAPlet.new @router = ::SOAP::RPC::Router.new(self.class.name)
self.level = Logger::Severity::INFO @soaplet = ::SOAP::RPC::SOAPlet.new(@router)
on_init on_init
@server = WEBrick::HTTPServer.new(@webrick_config)
@server.mount('/', @soaplet)
end end
def on_init def on_init
# define extra methods in derived class. # do extra initialization in a derived class if needed.
end end
def status def status
if @server @server.status if @server
@server.status
else
nil
end
end end
def shutdown def shutdown
@ -48,31 +47,39 @@ class HTTPServer < Logger::Application
end end
def mapping_registry def mapping_registry
@soaplet.app_scope_router.mapping_registry @router.mapping_registry
end end
def mapping_registry=(mapping_registry) def mapping_registry=(mapping_registry)
@soaplet.app_scope_router.mapping_registry = mapping_registry @router.mapping_registry = mapping_registry
end
def generate_explicit_type
@router.generate_explicit_type
end
def generate_explicit_type=(generate_explicit_type)
@router.generate_explicit_type = generate_explicit_type
end end
# servant entry interface # servant entry interface
def add_rpc_request_servant(factory, namespace = @default_namespace, def add_rpc_request_servant(factory, namespace = @default_namespace)
mapping_registry = nil) @router.add_rpc_request_servant(factory, namespace)
@soaplet.add_rpc_request_servant(factory, namespace, mapping_registry)
end end
def add_rpc_servant(obj, namespace = @default_namespace) def add_rpc_servant(obj, namespace = @default_namespace)
@soaplet.add_rpc_servant(obj, namespace) @router.add_rpc_servant(obj, namespace)
end end
def add_rpc_request_headerhandler(factory) def add_request_headerhandler(factory)
@soaplet.add_rpc_request_headerhandler(factory) @router.add_request_headerhandler(factory)
end end
def add_rpc_headerhandler(obj) def add_headerhandler(obj)
@soaplet.add_rpc_headerhandler(obj) @router.add_headerhandler(obj)
end end
alias add_rpc_headerhandler add_headerhandler
# method entry interface # method entry interface
@ -81,52 +88,38 @@ class HTTPServer < Logger::Application
end end
alias add_method add_rpc_method alias add_method add_rpc_method
def add_document_method(obj, name, req_qname, res_qname)
opt = {}
opt[:request_style] = opt[:response_style] = :document
opt[:request_use] = opt[:response_use] = :literal
param_def = [
['input', req_qname.name, [nil, req_qname.namespace, req_qname.name]],
['output', req_qname.name, [nil, res_qname.namespace, res_qname.name]]
]
@soaplet.app_scope_router.add_operation(req_qname, nil, obj, name,
param_def, opt)
end
def add_rpc_method_as(obj, name, name_as, *param) def add_rpc_method_as(obj, name, name_as, *param)
qname = XSD::QName.new(@default_namespace, name_as) qname = XSD::QName.new(@default_namespace, name_as)
soapaction = nil soapaction = nil
param_def = create_param_def(obj, name, param) param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
add_operation(qname, soapaction, obj, name, param_def) @router.add_rpc_operation(obj, qname, soapaction, name, param_def)
end end
alias add_method_as add_rpc_method_as alias add_method_as add_rpc_method_as
def add_operation(qname, soapaction, obj, name, param_def, opt = {}) def add_document_method(obj, soapaction, name, req_qnames, res_qnames)
opt[:request_style] ||= :rpc param_def = SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
opt[:response_style] ||= :rpc @router.add_document_operation(obj, soapaction, name, param_def)
opt[:request_use] ||= :encoded
opt[:response_use] ||= :encoded
@soaplet.app_scope_router.add_operation(qname, soapaction, obj, name,
param_def, opt)
end end
def create_param_def(obj, name, param = nil) def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
if param.nil? or param.empty? @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
method = obj.method(name)
::SOAP::RPC::SOAPMethod.create_param_def(
(1..method.arity.abs).collect { |i| "p#{i}" })
elsif param.size == 1 and param[0].is_a?(Array)
param[0]
else
::SOAP::RPC::SOAPMethod.create_param_def(param)
end end
def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {})
@router.add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt)
end
def add_document_operation(receiver, soapaction, name, param_def, opt = {})
@router.add_document_operation(receiver, soapaction, name, param_def, opt)
end
def add_document_request_operation(factory, soapaction, name, param_def, opt = {})
@router.add_document_request_operation(factory, soapaction, name, param_def, opt)
end end
private private
def run def run
@server = WEBrick::HTTPServer.new(@webrick_config)
@server.mount('/', @soaplet)
@server.start @server.start
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - RPC Proxy library. # SOAP4R - RPC Proxy library.
# Copyright (C) 2000, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -78,30 +78,62 @@ public
@streamhandler.test_loopback_response @streamhandler.test_loopback_response
end end
def add_rpc_method(qname, soapaction, name, param_def, opt = {}) def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
opt[:request_qname] = qname
opt[:request_style] ||= :rpc opt[:request_style] ||= :rpc
opt[:response_style] ||= :rpc opt[:response_style] ||= :rpc
opt[:request_use] ||= :encoded opt[:request_use] ||= :encoded
opt[:response_use] ||= :encoded opt[:response_use] ||= :encoded
@operation[name] = Operation.new(qname, soapaction, name, param_def, opt) @operation[name] = Operation.new(soapaction, param_def, opt)
end end
def add_document_method(qname, soapaction, name, param_def, opt = {}) def add_document_operation(soapaction, name, param_def, opt = {})
opt[:request_style] ||= :document opt[:request_style] ||= :document
opt[:response_style] ||= :document opt[:response_style] ||= :document
opt[:request_use] ||= :literal opt[:request_use] ||= :literal
opt[:response_use] ||= :literal opt[:response_use] ||= :literal
@operation[name] = Operation.new(qname, soapaction, name, param_def, opt) @operation[name] = Operation.new(soapaction, param_def, opt)
end end
# add_method is for shortcut of typical rpc/encoded method definition. # add_method is for shortcut of typical rpc/encoded method definition.
alias add_method add_rpc_method alias add_method add_rpc_operation
alias add_rpc_method add_rpc_operation
alias add_document_method add_document_operation
def invoke(req_header, req_body, opt = create_options) def invoke(req_header, req_body, opt = nil)
opt ||= create_options
route(req_header, req_body, opt, opt)
end
def call(name, *params)
unless op_info = @operation[name]
raise MethodDefinitionError, "method: #{name} not defined"
end
req_header = create_request_header
req_body = SOAPBody.new(
op_info.request_body(params, @mapping_registry, @literal_mapping_registry)
)
reqopt = create_options({
:soapaction => op_info.soapaction || @soapaction,
:default_encodingstyle => op_info.request_default_encodingstyle})
resopt = create_options({
:default_encodingstyle => op_info.response_default_encodingstyle})
env = route(req_header, req_body, reqopt, resopt)
raise EmptyResponseError unless env
receive_headers(env.header)
begin
check_fault(env.body)
rescue ::SOAP::FaultError => e
op_info.raise_fault(e, @mapping_registry, @literal_mapping_registry)
end
op_info.response_obj(env.body, @mapping_registry, @literal_mapping_registry)
end
def route(req_header, req_body, reqopt, resopt)
req_env = SOAPEnvelope.new(req_header, req_body) req_env = SOAPEnvelope.new(req_header, req_body)
opt[:external_content] = nil reqopt[:external_content] = nil
conn_data = marshal(req_env, opt) conn_data = marshal(req_env, reqopt)
if ext = opt[:external_content] if ext = reqopt[:external_content]
mime = MIMEMessage.new mime = MIMEMessage.new
ext.each do |k, v| ext.each do |k, v|
mime.add_attachment(v.data) mime.add_attachment(v.data)
@ -111,33 +143,12 @@ public
conn_data.send_string = mime.content_str conn_data.send_string = mime.content_str
conn_data.send_contenttype = mime.headers['content-type'].str conn_data.send_contenttype = mime.headers['content-type'].str
end end
conn_data = @streamhandler.send(@endpoint_url, conn_data, opt[:soapaction]) conn_data = @streamhandler.send(@endpoint_url, conn_data,
reqopt[:soapaction])
if conn_data.receive_string.empty? if conn_data.receive_string.empty?
return nil return nil
end end
unmarshal(conn_data, opt) unmarshal(conn_data, resopt)
end
def call(name, *params)
unless op_info = @operation[name]
raise MethodDefinitionError, "Method: #{name} not defined."
end
req_header = create_request_header
req_body = op_info.create_request_body(params, @mapping_registry,
@literal_mapping_registry)
opt = create_options({
:soapaction => op_info.soapaction || @soapaction,
:default_encodingstyle => op_info.response_default_encodingstyle})
env = invoke(req_header, req_body, opt)
receive_headers(env.header)
raise EmptyResponseError.new("Empty response.") unless env
begin
check_fault(env.body)
rescue ::SOAP::FaultError => e
Mapping.fault2exception(e)
end
op_info.create_response_obj(env, @mapping_registry,
@literal_mapping_registry)
end end
def check_fault(body) def check_fault(body)
@ -217,98 +228,203 @@ private
attr_reader :request_use attr_reader :request_use
attr_reader :response_use attr_reader :response_use
def initialize(qname, soapaction, name, param_def, opt) def initialize(soapaction, param_def, opt)
@soapaction = soapaction @soapaction = soapaction
@request_style = opt[:request_style] @request_style = opt[:request_style]
@response_style = opt[:response_style] @response_style = opt[:response_style]
@request_use = opt[:request_use] @request_use = opt[:request_use]
@response_use = opt[:response_use] @response_use = opt[:response_use]
@rpc_method_factory = @document_method_name = nil
check_style(@request_style) check_style(@request_style)
check_style(@response_style) check_style(@response_style)
check_use(@request_use)
check_use(@response_use)
if @request_style == :rpc if @request_style == :rpc
@rpc_method_factory = SOAPMethodRequest.new(qname, param_def, @rpc_request_qname = opt[:request_qname]
@soapaction) if @rpc_request_qname.nil?
raise MethodDefinitionError.new("rpc_request_qname must be given")
end
@rpc_method_factory =
RPC::SOAPMethodRequest.new(@rpc_request_qname, param_def, @soapaction)
else else
@document_method_name = {} @doc_request_qnames = []
@doc_response_qnames = []
param_def.each do |inout, paramname, typeinfo| param_def.each do |inout, paramname, typeinfo|
klass, namespace, name = typeinfo klass_not_used, nsdef, namedef = typeinfo
case inout.to_s if namedef.nil?
when "input" raise MethodDefinitionError.new("qname must be given")
@document_method_name[:input] = ::XSD::QName.new(namespace, name) end
when "output" case inout
@document_method_name[:output] = ::XSD::QName.new(namespace, name) when SOAPMethod::IN
@doc_request_qnames << XSD::QName.new(nsdef, namedef)
when SOAPMethod::OUT
@doc_response_qnames << XSD::QName.new(nsdef, namedef)
else else
raise MethodDefinitionError, "unknown type: " + inout raise MethodDefinitionError.new(
"illegal inout definition for document style: #{inout}")
end end
end end
end end
end end
def request_default_encodingstyle def request_default_encodingstyle
(@request_style == :rpc) ? EncodingNamespace : LiteralNamespace (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
end end
def response_default_encodingstyle def response_default_encodingstyle
(@response_style == :rpc) ? EncodingNamespace : LiteralNamespace (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
end end
# for rpc def request_body(values, mapping_registry, literal_mapping_registry)
def each_param_name(*target)
if @request_style == :rpc if @request_style == :rpc
@rpc_method_factory.each_param_name(*target) do |name| request_rpc(values, mapping_registry, literal_mapping_registry)
yield(name)
end
else else
yield(@document_method_name[:input].name) request_doc(values, mapping_registry, literal_mapping_registry)
end end
end end
def create_request_body(values, mapping_registry, literal_mapping_registry) def response_obj(body, mapping_registry, literal_mapping_registry)
if @request_style == :rpc
values = Mapping.obj2soap(values, mapping_registry).to_a
method = @rpc_method_factory.dup
params = {}
idx = 0
method.each_param_name(::SOAP::RPC::SOAPMethod::IN,
::SOAP::RPC::SOAPMethod::INOUT) do |name|
params[name] = values[idx] || SOAPNil.new
idx += 1
end
method.set_param(params)
SOAPBody.new(method)
else
name = @document_method_name[:input]
document = literal_mapping_registry.obj2soap(values[0], name)
SOAPBody.new(document)
end
end
def create_response_obj(env, mapping_registry, literal_mapping_registry)
if @response_style == :rpc if @response_style == :rpc
ret = env.body.response ? response_rpc(body, mapping_registry, literal_mapping_registry)
Mapping.soap2obj(env.body.response, mapping_registry) : nil
if env.body.outparams
outparams = env.body.outparams.collect { |outparam|
Mapping.soap2obj(outparam)
}
[ret].concat(outparams)
else else
ret response_doc(body, mapping_registry, literal_mapping_registry)
end end
end
def raise_fault(e, mapping_registry, literal_mapping_registry)
if @response_style == :rpc
Mapping.fault2exception(e, mapping_registry)
else else
Mapping.soap2obj(env.body.root_node, literal_mapping_registry) Mapping.fault2exception(e, literal_mapping_registry)
end end
end end
private private
ALLOWED_STYLE = [:rpc, :document]
def check_style(style) def check_style(style)
unless ALLOWED_STYLE.include?(style) unless [:rpc, :document].include?(style)
raise MethodDefinitionError, "unknown style: " + style raise MethodDefinitionError.new("unknown style: #{style}")
end end
end end
def check_use(use)
unless [:encoded, :literal].include?(use)
raise MethodDefinitionError.new("unknown use: #{use}")
end
end
def request_rpc(values, mapping_registry, literal_mapping_registry)
if @request_use == :encoded
request_rpc_enc(values, mapping_registry)
else
request_rpc_lit(values, literal_mapping_registry)
end
end
def request_doc(values, mapping_registry, literal_mapping_registry)
if @request_use == :encoded
request_doc_enc(values, mapping_registry)
else
request_doc_lit(values, literal_mapping_registry)
end
end
def request_rpc_enc(values, mapping_registry)
method = @rpc_method_factory.dup
names = method.input_params
obj = create_request_obj(names, values)
soap = Mapping.obj2soap(obj, mapping_registry, @rpc_request_qname)
method.set_param(soap)
method
end
def request_rpc_lit(values, mapping_registry)
method = @rpc_method_factory.dup
params = {}
idx = 0
method.input_params.each do |name|
params[name] = Mapping.obj2soap(values[idx], mapping_registry,
XSD::QName.new(nil, name))
idx += 1
end
method.set_param(params)
method
end
def request_doc_enc(values, mapping_registry)
(0...values.size).collect { |idx|
ele = Mapping.obj2soap(values[idx], mapping_registry)
ele.elename = @doc_request_qnames[idx]
ele
}
end
def request_doc_lit(values, mapping_registry)
(0...values.size).collect { |idx|
ele = Mapping.obj2soap(values[idx], mapping_registry,
@doc_request_qnames[idx])
ele.encodingstyle = LiteralNamespace
ele
}
end
def response_rpc(body, mapping_registry, literal_mapping_registry)
if @response_use == :encoded
response_rpc_enc(body, mapping_registry)
else
response_rpc_lit(body, literal_mapping_registry)
end
end
def response_doc(body, mapping_registry, literal_mapping_registry)
if @response_use == :encoded
return *response_doc_enc(body, mapping_registry)
else
return *response_doc_lit(body, literal_mapping_registry)
end
end
def response_rpc_enc(body, mapping_registry)
ret = nil
if body.response
ret = Mapping.soap2obj(body.response, mapping_registry,
@rpc_method_factory.retval_class_name)
end
if body.outparams
outparams = body.outparams.collect { |outparam|
Mapping.soap2obj(outparam, mapping_regisry)
}
[ret].concat(outparams)
else
ret
end
end
def response_rpc_lit(body, mapping_registry)
body.root_node.collect { |key, value|
Mapping.soap2obj(value, mapping_registry,
@rpc_method_factory.retval_class_name)
}
end
def response_doc_enc(body, mapping_registry)
body.collect { |key, value|
Mapping.soap2obj(value, mapping_registry)
}
end
def response_doc_lit(body, mapping_registry)
body.collect { |key, value|
Mapping.soap2obj(value, mapping_registry)
}
end
def create_request_obj(names, params)
o = Object.new
for idx in 0 ... params.length
o.instance_variable_set('@' + names[idx], params[idx])
end
o
end
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - RPC Routing library # SOAP4R - RPC Routing library
# Copyright (C) 2001, 2002 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2001, 2002, 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -25,101 +25,229 @@ class Router
include SOAP include SOAP
attr_reader :actor attr_reader :actor
attr_accessor :allow_unqualified_element
attr_accessor :default_encodingstyle
attr_accessor :mapping_registry attr_accessor :mapping_registry
attr_accessor :literal_mapping_registry attr_accessor :literal_mapping_registry
attr_reader :headerhandler attr_accessor :generate_explicit_type
def initialize(actor) def initialize(actor)
@actor = actor @actor = actor
@allow_unqualified_element = false
@default_encodingstyle = nil
@mapping_registry = nil @mapping_registry = nil
@headerhandler = Header::HandlerSet.new @headerhandler = Header::HandlerSet.new
@literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new
@operation = {} @generate_explicit_type = true
@operation_by_soapaction = {}
@operation_by_qname = {}
@headerhandlerfactory = []
end end
def add_rpc_method(receiver, qname, soapaction, name, param_def, opt = {}) ###
opt[:request_style] ||= :rpc ## header handler interface
opt[:response_style] ||= :rpc #
opt[:request_use] ||= :encoded def add_request_headerhandler(factory)
opt[:response_use] ||= :encoded unless factory.respond_to?(:create)
add_operation(qname, soapaction, receiver, name, param_def, opt) raise TypeError.new("factory must respond to 'create'")
end
@headerhandlerfactory << factory
end end
def add_document_method(receiver, qname, soapaction, name, param_def, opt = {}) def add_headerhandler(handler)
opt[:request_style] ||= :document @headerhandler.add(handler)
opt[:response_style] ||= :document
opt[:request_use] ||= :encoded
opt[:response_use] ||= :encoded
if opt[:request_style] == :document
inputdef = param_def.find { |inout, paramname, typeinfo| inout == "input" }
klass, nsdef, namedef = inputdef[2]
qname = ::XSD::QName.new(nsdef, namedef)
end
add_operation(qname, soapaction, receiver, name, param_def, opt)
end end
def add_operation(qname, soapaction, receiver, name, param_def, opt) ###
@operation[fqname(qname)] = Operation.new(qname, soapaction, receiver, ## servant definition interface
name, param_def, opt) #
def add_rpc_request_servant(factory, namespace)
unless factory.respond_to?(:create)
raise TypeError.new("factory must respond to 'create'")
end
obj = factory.create # a dummy instance for introspection
::SOAP::RPC.defined_methods(obj).each do |name|
begin
qname = XSD::QName.new(namespace, name)
param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name)
opt = create_styleuse_option(:rpc, :encoded)
add_rpc_request_operation(factory, qname, nil, name, param_def, opt)
rescue SOAP::RPC::MethodDefinitionError => e
p e if $DEBUG
end
end
end end
# add_method is for shortcut of typical use="encoded" method definition. def add_rpc_servant(obj, namespace)
alias add_method add_rpc_method ::SOAP::RPC.defined_methods(obj).each do |name|
begin
qname = XSD::QName.new(namespace, name)
param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name)
opt = create_styleuse_option(:rpc, :encoded)
add_rpc_operation(obj, qname, nil, name, param_def, opt)
rescue SOAP::RPC::MethodDefinitionError => e
p e if $DEBUG
end
end
end
alias add_servant add_rpc_servant
###
## operation definition interface
#
def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
ensure_styleuse_option(opt, :rpc, :encoded)
opt[:request_qname] = qname
op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def,
opt)
if opt[:request_style] != :rpc
raise RPCRoutingError.new("illegal request_style given")
end
assign_operation(soapaction, qname, op)
end
alias add_method add_rpc_operation
alias add_rpc_method add_rpc_operation
def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {})
ensure_styleuse_option(opt, :rpc, :encoded)
opt[:request_qname] = qname
op = RequestScopeOperation.new(soapaction, factory, name, param_def, opt)
if opt[:request_style] != :rpc
raise RPCRoutingError.new("illegal request_style given")
end
assign_operation(soapaction, qname, op)
end
def add_document_operation(receiver, soapaction, name, param_def, opt = {})
#
# adopt workaround for doc/lit wrapper method
# (you should consider to simply use rpc/lit service)
#
#unless soapaction
# raise RPCRoutingError.new("soapaction is a must for document method")
#end
ensure_styleuse_option(opt, :document, :literal)
op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def,
opt)
if opt[:request_style] != :document
raise RPCRoutingError.new("illegal request_style given")
end
assign_operation(soapaction, first_input_part_qname(param_def), op)
end
alias add_document_method add_document_operation
def add_document_request_operation(factory, soapaction, name, param_def, opt = {})
#
# adopt workaround for doc/lit wrapper method
# (you should consider to simply use rpc/lit service)
#
#unless soapaction
# raise RPCRoutingError.new("soapaction is a must for document method")
#end
ensure_styleuse_option(opt, :document, :literal)
op = RequestScopeOperation.new(soapaction, receiver, name, param_def, opt)
if opt[:request_style] != :document
raise RPCRoutingError.new("illegal request_style given")
end
assign_operation(soapaction, first_input_part_qname(param_def), op)
end
def route(conn_data) def route(conn_data)
soap_response = nil # we cannot set request_default_encodingsyle before parsing the content.
begin
env = unmarshal(conn_data) env = unmarshal(conn_data)
if env.nil? if env.nil?
raise ArgumentError.new("Illegal SOAP marshal format.") raise ArgumentError.new("illegal SOAP marshal format")
end end
receive_headers(env.header) op = lookup_operation(conn_data.soapaction, env.body)
request = env.body.request headerhandler = @headerhandler.dup
op = @operation[fqname(request.elename)] @headerhandlerfactory.each do |f|
unless op headerhandler.add(f.create)
raise RPCRoutingError.new("Method: #{request.elename} not supported.")
end end
soap_response = op.call(request, @mapping_registry, @literal_mapping_registry) receive_headers(headerhandler, env.header)
soap_response = default_encodingstyle = nil
begin
soap_response =
op.call(env.body, @mapping_registry, @literal_mapping_registry)
default_encodingstyle = op.response_default_encodingstyle
rescue Exception rescue Exception
soap_response = fault($!) soap_response = fault($!)
conn_data.is_fault = true default_encodingstyle = nil
end end
marshal(conn_data, op, soap_response) conn_data.is_fault = true if soap_response.is_a?(SOAPFault)
conn_data header = call_headers(headerhandler)
body = SOAPBody.new(soap_response)
env = SOAPEnvelope.new(header, body)
marshal(conn_data, env, default_encodingstyle)
end end
# Create fault response string. # Create fault response string.
def create_fault_response(e, charset = nil) def create_fault_response(e)
header = SOAPHeader.new env = SOAPEnvelope.new(SOAPHeader.new, SOAPBody.new(fault(e)))
body = SOAPBody.new(fault(e)) opt = {}
env = SOAPEnvelope.new(header, body)
opt = options
opt[:external_content] = nil opt[:external_content] = nil
opt[:charset] = charset
response_string = Processor.marshal(env, opt) response_string = Processor.marshal(env, opt)
conn_data = StreamHandler::ConnectionData.new(response_string) conn_data = StreamHandler::ConnectionData.new(response_string)
conn_data.is_fault = true conn_data.is_fault = true
if ext = opt[:external_content] if ext = opt[:external_content]
mime = MIMEMessage.new mimeize(conn_data, ext)
ext.each do |k, v|
mime.add_attachment(v.data)
end
mime.add_part(conn_data.send_string + "\r\n")
mime.close
conn_data.send_string = mime.content_str
conn_data.send_contenttype = mime.headers['content-type'].str
end end
conn_data conn_data
end end
private private
def call_headers def first_input_part_qname(param_def)
headers = @headerhandler.on_outbound param_def.each do |inout, paramname, typeinfo|
if inout == SOAPMethod::IN
klass, nsdef, namedef = typeinfo
return XSD::QName.new(nsdef, namedef)
end
end
nil
end
def create_styleuse_option(style, use)
opt = {}
opt[:request_style] = opt[:response_style] = style
opt[:request_use] = opt[:response_use] = use
opt
end
def ensure_styleuse_option(opt, style, use)
opt[:request_style] ||= style
opt[:response_style] ||= style
opt[:request_use] ||= use
opt[:response_use] ||= use
end
def assign_operation(soapaction, qname, op)
assigned = false
if soapaction and !soapaction.empty?
@operation_by_soapaction[soapaction] = op
assigned = true
end
if qname
@operation_by_qname[qname] = op
assigned = true
end
unless assigned
raise RPCRoutingError.new("cannot assign operation")
end
end
def lookup_operation(soapaction, body)
if op = @operation_by_soapaction[soapaction]
return op
end
qname = body.root_node.elename
if op = @operation_by_qname[qname]
return op
end
if soapaction
raise RPCRoutingError.new("operation: #{soapaction} not supported")
else
raise RPCRoutingError.new("operation: #{qname} not supported")
end
end
def call_headers(headerhandler)
headers = headerhandler.on_outbound
if headers.empty? if headers.empty?
nil nil
else else
@ -131,12 +259,12 @@ private
end end
end end
def receive_headers(headers) def receive_headers(headerhandler, headers)
@headerhandler.on_inbound(headers) if headers headerhandler.on_inbound(headers) if headers
end end
def unmarshal(conn_data) def unmarshal(conn_data)
opt = options opt = {}
contenttype = conn_data.receive_contenttype contenttype = conn_data.receive_contenttype
if /#{MIMEMessage::MultipartContentType}/i =~ contenttype if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
opt[:external_content] = {} opt[:external_content] = {}
@ -160,19 +288,20 @@ private
env env
end end
def marshal(conn_data, op, soap_response) def marshal(conn_data, env, default_encodingstyle = nil)
response_opt = options opt = {}
response_opt[:external_content] = nil opt[:external_content] = nil
if op and !conn_data.is_fault and op.response_use == :document opt[:default_encodingstyle] = default_encodingstyle
response_opt[:default_encodingstyle] = opt[:generate_explicit_type] = @generate_explicit_type
::SOAP::EncodingStyle::ASPDotNetHandler::Namespace response_string = Processor.marshal(env, opt)
end
header = call_headers
body = SOAPBody.new(soap_response)
env = SOAPEnvelope.new(header, body)
response_string = Processor.marshal(env, response_opt)
conn_data.send_string = response_string conn_data.send_string = response_string
if ext = response_opt[:external_content] if ext = opt[:external_content]
mimeize(conn_data, ext)
end
conn_data
end
def mimeize(conn_data, ext)
mime = MIMEMessage.new mime = MIMEMessage.new
ext.each do |k, v| ext.each do |k, v|
mime.add_attachment(v.data) mime.add_attachment(v.data)
@ -181,7 +310,7 @@ private
mime.close mime.close
conn_data.send_string = mime.content_str conn_data.send_string = mime.content_str
conn_data.send_contenttype = mime.headers['content-type'].str conn_data.send_contenttype = mime.headers['content-type'].str
end conn_data
end end
# Create fault response. # Create fault response.
@ -194,21 +323,7 @@ private
Mapping.obj2soap(detail, @mapping_registry)) Mapping.obj2soap(detail, @mapping_registry))
end end
def fqname(qname)
"#{ qname.namespace }:#{ qname.name }"
end
def options
opt = {}
opt[:default_encodingstyle] = @default_encodingstyle
if @allow_unqualified_element
opt[:allow_unqualified_element] = true
end
opt
end
class Operation class Operation
attr_reader :receiver
attr_reader :name attr_reader :name
attr_reader :soapaction attr_reader :soapaction
attr_reader :request_style attr_reader :request_style
@ -216,62 +331,148 @@ private
attr_reader :request_use attr_reader :request_use
attr_reader :response_use attr_reader :response_use
def initialize(qname, soapaction, receiver, name, param_def, opt) def initialize(soapaction, name, param_def, opt)
@soapaction = soapaction @soapaction = soapaction
@receiver = receiver
@name = name @name = name
@request_style = opt[:request_style] @request_style = opt[:request_style]
@response_style = opt[:response_style] @response_style = opt[:response_style]
@request_use = opt[:request_use] @request_use = opt[:request_use]
@response_use = opt[:response_use] @response_use = opt[:response_use]
check_style(@request_style)
check_style(@response_style)
check_use(@request_use)
check_use(@response_use)
if @response_style == :rpc if @response_style == :rpc
@rpc_response_factory = request_qname = opt[:request_qname] or raise
RPC::SOAPMethodRequest.new(qname, param_def, @soapaction) @rpc_method_factory =
RPC::SOAPMethodRequest.new(request_qname, param_def, @soapaction)
@rpc_response_qname = opt[:response_qname]
else else
outputdef = param_def.find { |inout, paramname, typeinfo| inout == "output" } @doc_request_qnames = []
klass, nsdef, namedef = outputdef[2] @doc_response_qnames = []
@document_response_qname = ::XSD::QName.new(nsdef, namedef) param_def.each do |inout, paramname, typeinfo|
klass, nsdef, namedef = typeinfo
case inout
when SOAPMethod::IN
@doc_request_qnames << XSD::QName.new(nsdef, namedef)
when SOAPMethod::OUT
@doc_response_qnames << XSD::QName.new(nsdef, namedef)
else
raise ArgumentError.new(
"illegal inout definition for document style: #{inout}")
end
end
end end
end end
def call(request, mapping_registry, literal_mapping_registry) def request_default_encodingstyle
if @request_style == :rpc (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
param = Mapping.soap2obj(request, mapping_registry)
result = rpc_call(request, param)
else
param = Mapping.soap2obj(request, literal_mapping_registry)
result = document_call(request, param)
end end
if @response_style == :rpc
rpc_response(result, mapping_registry) def response_default_encodingstyle
(@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
def call(body, mapping_registry, literal_mapping_registry)
if @request_style == :rpc
values = request_rpc(body, mapping_registry, literal_mapping_registry)
else else
document_response(result, literal_mapping_registry) values = request_document(body, mapping_registry, literal_mapping_registry)
end
result = receiver.method(@name.intern).call(*values)
return result if result.is_a?(SOAPFault)
if @response_style == :rpc
response_rpc(result, mapping_registry, literal_mapping_registry)
else
response_doc(result, mapping_registry, literal_mapping_registry)
end end
end end
private private
def rpc_call(request, param) def receiver
raise NotImplementedError.new('must be defined in derived class')
end
def request_rpc(body, mapping_registry, literal_mapping_registry)
request = body.request
unless request.is_a?(SOAPStruct) unless request.is_a?(SOAPStruct)
raise RPCRoutingError.new("Not an RPC style.") raise RPCRoutingError.new("not an RPC style")
end
if @request_use == :encoded
request_rpc_enc(request, mapping_registry)
else
request_rpc_lit(request, literal_mapping_registry)
end end
values = request.collect { |key, value| param[key] }
@receiver.method(@name.intern).call(*values)
end end
def document_call(request, param) def request_document(body, mapping_registry, literal_mapping_registry)
@receiver.method(@name.intern).call(param) # ToDo: compare names with @doc_request_qnames
if @request_use == :encoded
request_doc_enc(body, mapping_registry)
else
request_doc_lit(body, literal_mapping_registry)
end
end end
def rpc_response(result, mapping_registry) def request_rpc_enc(request, mapping_registry)
soap_response = @rpc_response_factory.create_method_response param = Mapping.soap2obj(request, mapping_registry)
request.collect { |key, value|
param[key]
}
end
def request_rpc_lit(request, mapping_registry)
request.collect { |key, value|
Mapping.soap2obj(value, mapping_registry)
}
end
def request_doc_enc(body, mapping_registry)
body.collect { |key, value|
Mapping.soap2obj(value, mapping_registry)
}
end
def request_doc_lit(body, mapping_registry)
body.collect { |key, value|
Mapping.soap2obj(value, mapping_registry)
}
end
def response_rpc(result, mapping_registry, literal_mapping_registry)
if @response_use == :encoded
response_rpc_enc(result, mapping_registry)
else
response_rpc_lit(result, literal_mapping_registry)
end
end
def response_doc(result, mapping_registry, literal_mapping_registry)
if @doc_response_qnames.size == 1 and !result.is_a?(Array)
result = [result]
end
if result.size != @doc_response_qnames.size
raise "required #{@doc_response_qnames.size} responses " +
"but #{result.size} given"
end
if @response_use == :encoded
response_doc_enc(result, mapping_registry)
else
response_doc_lit(result, literal_mapping_registry)
end
end
def response_rpc_enc(result, mapping_registry)
soap_response =
@rpc_method_factory.create_method_response(@rpc_response_qname)
if soap_response.have_outparam? if soap_response.have_outparam?
unless result.is_a?(Array) unless result.is_a?(Array)
raise RPCRoutingError.new("Out parameter was not returned.") raise RPCRoutingError.new("out parameter was not returned")
end end
outparams = {} outparams = {}
i = 1 i = 1
soap_response.each_param_name('out', 'inout') do |outparam| soap_response.output_params.each do |outparam|
outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry) outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry)
i += 1 i += 1
end end
@ -283,8 +484,83 @@ private
soap_response soap_response
end end
def document_response(result, literal_mapping_registry) def response_rpc_lit(result, mapping_registry)
literal_mapping_registry.obj2soap(result, @document_response_qname) soap_response =
@rpc_method_factory.create_method_response(@rpc_response_qname)
if soap_response.have_outparam?
unless result.is_a?(Array)
raise RPCRoutingError.new("out parameter was not returned")
end
outparams = {}
i = 1
soap_response.output_params.each do |outparam|
outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry,
XSD::QName.new(nil, outparam))
i += 1
end
soap_response.set_outparam(outparams)
soap_response.retval = Mapping.obj2soap(result[0], mapping_registry,
XSD::QName.new(nil, soap_response.elename))
else
soap_response.retval = Mapping.obj2soap(result, mapping_registry,
XSD::QName.new(nil, soap_response.elename))
end
soap_response
end
def response_doc_enc(result, mapping_registry)
(0...result.size).collect { |idx|
ele = Mapping.obj2soap(result[idx], mapping_registry)
ele.elename = @doc_response_qnames[idx]
ele
}
end
def response_doc_lit(result, mapping_registry)
(0...result.size).collect { |idx|
mapping_registry.obj2soap(result[idx], @doc_response_qnames[idx])
}
end
def check_style(style)
unless [:rpc, :document].include?(style)
raise ArgumentError.new("unknown style: #{style}")
end
end
def check_use(use)
unless [:encoded, :literal].include?(use)
raise ArgumentError.new("unknown use: #{use}")
end
end
end
class ApplicationScopeOperation < Operation
def initialize(soapaction, receiver, name, param_def, opt)
super(soapaction, name, param_def, opt)
@receiver = receiver
end
private
def receiver
@receiver
end
end
class RequestScopeOperation < Operation
def initialize(soapaction, receiver_factory, name, param_def, opt)
super(soapaction, name, param_def, opt)
unless receiver_factory.respond_to?(:create)
raise TypeError.new("factory must respond to 'create'")
end
@receiver_factory = receiver_factory
end
private
def receiver
@receiver_factory.create
end end
end end
end end

View File

@ -1,5 +1,5 @@
# SOAP4R - SOAP handler servlet for WEBrick # SOAP4R - SOAP handler servlet for WEBrick
# Copyright (C) 2001, 2002, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2001-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -14,7 +14,23 @@ begin
require 'stringio' require 'stringio'
require 'zlib' require 'zlib'
rescue LoadError rescue LoadError
STDERR.puts "Loading stringio or zlib failed. No gzipped response support." if $DEBUG warn("Loading stringio or zlib failed. No gzipped response supported.") if $DEBUG
end
warn("Overriding WEBrick::Log#debug") if $DEBUG
require 'webrick/log'
module WEBrick
class Log < BasicLog
alias __debug debug
def debug(msg = nil)
if block_given? and msg.nil?
__debug(yield)
else
__debug(msg)
end
end
end
end end
@ -24,61 +40,28 @@ module RPC
class SOAPlet < WEBrick::HTTPServlet::AbstractServlet class SOAPlet < WEBrick::HTTPServlet::AbstractServlet
public public
attr_reader :app_scope_router
attr_reader :options attr_reader :options
def initialize def initialize(router = nil)
@rpc_router_map = {} @router = router || ::SOAP::RPC::Router.new(self.class.name)
@app_scope_router = ::SOAP::RPC::Router.new(self.class.name)
@headerhandlerfactory = []
@app_scope_headerhandler = nil
@options = {} @options = {}
@config = {}
end
# for backward compatibility
def app_scope_router
@router
end
# for backward compatibility
def add_servant(obj, namespace)
@router.add_rpc_servant(obj, namespace)
end end
def allow_content_encoding_gzip=(allow) def allow_content_encoding_gzip=(allow)
@options[:allow_content_encoding_gzip] = allow @options[:allow_content_encoding_gzip] = allow
end end
# Add servant factory whose object has request scope. A servant object is
# instanciated for each request.
#
# Bear in mind that servant factories are distinguished by HTTP SOAPAction
# header in request. Client which calls request-scoped servant must have a
# SOAPAction header which is a namespace of the servant factory.
# I mean, use Driver#add_method_with_soapaction instead of Driver#add_method
# at client side.
#
# A factory must respond to :create.
#
def add_rpc_request_servant(factory, namespace, mapping_registry = nil)
unless factory.respond_to?(:create)
raise TypeError.new("factory must respond to 'create'")
end
router = setup_rpc_request_router(namespace)
router.factory = factory
router.mapping_registry = mapping_registry
end
# Add servant object which has application scope.
def add_rpc_servant(obj, namespace)
router = @app_scope_router
SOAPlet.add_rpc_servant_to_router(router, obj, namespace)
add_rpc_router(namespace, router)
end
alias add_servant add_rpc_servant
def add_rpc_request_headerhandler(factory)
unless factory.respond_to?(:create)
raise TypeError.new("factory must respond to 'create'")
end
@headerhandlerfactory << factory
end
def add_rpc_headerhandler(obj)
@app_scope_headerhandler = obj
end
alias add_headerhandler add_rpc_headerhandler
### ###
## Servlet interfaces for WEBrick. ## Servlet interfaces for WEBrick.
# #
@ -93,19 +76,43 @@ public
def do_GET(req, res) def do_GET(req, res)
res.header['Allow'] = 'POST' res.header['Allow'] = 'POST'
raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed." raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed"
end end
def do_POST(req, res) def do_POST(req, res)
@config[:Logger].debug { "SOAP request: " + req.body } logger.debug { "SOAP request: " + req.body } if logger
soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
router = lookup_router(soapaction)
with_headerhandler(router) do |router|
begin begin
conn_data = ::SOAP::StreamHandler::ConnectionData.new conn_data = ::SOAP::StreamHandler::ConnectionData.new
setup_req(conn_data, req)
conn_data = @router.route(conn_data)
setup_res(conn_data, req, res)
rescue Exception => e
conn_data = @router.create_fault_response(e)
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
res.body = conn_data.send_string
res['content-type'] = conn_data.send_contenttype || "text/xml"
end
if res.body.is_a?(IO)
res.chunked = true
logger.debug { "SOAP response: (chunked response not logged)" } if logger
else
logger.debug { "SOAP response: " + res.body } if logger
end
end
private
def logger
@config[:Logger]
end
def setup_req(conn_data, req)
conn_data.receive_string = req.body conn_data.receive_string = req.body
conn_data.receive_contenttype = req['content-type'] conn_data.receive_contenttype = req['content-type']
conn_data = router.route(conn_data) conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
end
def setup_res(conn_data, req, res)
res['content-type'] = conn_data.send_contenttype res['content-type'] = conn_data.send_contenttype
if conn_data.is_fault if conn_data.is_fault
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
@ -117,87 +124,15 @@ public
else else
res.body = conn_data.send_string res.body = conn_data.send_string
end end
rescue Exception => e
conn_data = router.create_fault_response(e)
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
res.body = conn_data.send_string
res['content-type'] = conn_data.send_contenttype || "text/xml"
end
end
if res.body.is_a?(IO)
res.chunked = true
@config[:Logger].debug { "SOAP response: (chunked response not logged)" }
else
@config[:Logger].debug { "SOAP response: " + res.body }
end
end
private
class RequestRouter < ::SOAP::RPC::Router
attr_accessor :factory
def initialize(style = :rpc, namespace = nil)
super(namespace)
@style = style
@namespace = namespace
@factory = nil
end
def route(soap_string)
obj = @factory.create
namespace = self.actor
router = ::SOAP::RPC::Router.new(@namespace)
if @style == :rpc
SOAPlet.add_rpc_servant_to_router(router, obj, namespace)
else
raise RuntimeError.new("'document' style not supported.")
end
router.route(soap_string)
end
end
def setup_rpc_request_router(namespace)
router = @rpc_router_map[namespace] || RequestRouter.new(:rpc, namespace)
add_rpc_router(namespace, router)
router
end
def add_rpc_router(namespace, router)
@rpc_router_map[namespace] = router
end end
def parse_soapaction(soapaction) def parse_soapaction(soapaction)
if /^"(.*)"$/ =~ soapaction if !soapaction.nil? and !soapaction.empty?
soapaction = $1 if /^"(.+)"$/ =~ soapaction
end return $1
if soapaction.empty?
return nil
end
soapaction
end
def lookup_router(namespace)
if namespace
@rpc_router_map[namespace] || @app_scope_router
else
@app_scope_router
end end
end end
nil
def with_headerhandler(router)
if @app_scope_headerhandler and
!router.headerhandler.include?(@app_scope_headerhandler)
router.headerhandler.add(@app_scope_headerhandler)
end
handlers = @headerhandlerfactory.collect { |f| f.create }
begin
handlers.each { |h| router.headerhandler.add(h) }
yield(router)
ensure
handlers.each { |h| router.headerhandler.delete(h) }
end
end end
def encode_gzip(req, outstring) def encode_gzip(req, outstring)
@ -219,32 +154,6 @@ private
req['accept-encoding'] and req['accept-encoding'] and
req['accept-encoding'].split(/,\s*/).include?('gzip') req['accept-encoding'].split(/,\s*/).include?('gzip')
end end
class << self
public
def add_rpc_servant_to_router(router, obj, namespace)
::SOAP::RPC.defined_methods(obj).each do |name|
begin
add_rpc_servant_method_to_router(router, obj, namespace, name)
rescue SOAP::RPC::MethodDefinitionError => e
p e if $DEBUG
end
end
end
def add_rpc_servant_method_to_router(router, obj, namespace, name,
style = :rpc, use = :encoded)
qname = XSD::QName.new(namespace, name)
soapaction = nil
method = obj.method(name)
param_def = ::SOAP::RPC::SOAPMethod.create_param_def(
(1..method.arity.abs).collect { |i| "p#{ i }" })
opt = {}
opt[:request_style] = opt[:response_style] = style
opt[:request_use] = opt[:response_use] = use
router.add_operation(qname, soapaction, obj, name, param_def, opt)
end
end
end end

View File

@ -13,7 +13,7 @@ require 'xsd/charset'
module SOAP module SOAP
Version = '1.5.3-ruby1.8.2' VERSION = Version = '1.5.4'
PropertyName = 'soap/property' PropertyName = 'soap/property'
EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/' EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/'
@ -75,6 +75,7 @@ class ArrayIndexOutOfBoundsError < Error; end
class ArrayStoreError < Error; end class ArrayStoreError < Error; end
class RPCRoutingError < Error; end class RPCRoutingError < Error; end
class EmptyResponseError < Error; end
class UnhandledMustUnderstandHeaderError < Error; end class UnhandledMustUnderstandHeaderError < Error; end
@ -101,6 +102,7 @@ class FaultError < Error
end end
end end
module Env module Env
def self.getenv(name) def self.getenv(name)
ENV[name.downcase] || ENV[name.upcase] ENV[name.downcase] || ENV[name.upcase]
@ -113,3 +115,25 @@ end
end end
unless Object.respond_to?(:instance_variable_get)
class Object
def instance_variable_get(ivarname)
instance_eval(ivarname)
end
def instance_variable_set(ivarname, value)
instance_eval("#{ivarname} = value")
end
end
end
unless Kernel.respond_to?(:warn)
module Kernel
def warn(msg)
STDERR.puts(msg + "\n") unless $VERBOSE.nil?
end
end
end

View File

@ -7,12 +7,12 @@
require 'soap/soap' require 'soap/soap'
require 'soap/property' require 'soap/httpconfigloader'
begin begin
require 'stringio' require 'stringio'
require 'zlib' require 'zlib'
rescue LoadError rescue LoadError
STDERR.puts "Loading stringio or zlib failed. No gzipped response support." if $DEBUG warn("Loading stringio or zlib failed. No gzipped response support.") if $DEBUG
end end
@ -27,7 +27,7 @@ class StreamHandler
end end
HTTPAccess2::Client HTTPAccess2::Client
rescue LoadError rescue LoadError
STDERR.puts "Loading http-access2 failed. Net/http is used." if $DEBUG warn("Loading http-access2 failed. Net/http is used.") if $DEBUG
require 'soap/netHttpClient' require 'soap/netHttpClient'
SOAP::NetHttpClient SOAP::NetHttpClient
end end
@ -40,6 +40,7 @@ class StreamHandler
attr_accessor :receive_string attr_accessor :receive_string
attr_accessor :receive_contenttype attr_accessor :receive_contenttype
attr_accessor :is_fault attr_accessor :is_fault
attr_accessor :soapaction
def initialize(send_string = nil) def initialize(send_string = nil)
@send_string = send_string @send_string = send_string
@ -47,6 +48,7 @@ class StreamHandler
@receive_string = nil @receive_string = nil
@receive_contenttype = nil @receive_contenttype = nil
@is_fault = false @is_fault = false
@soapaction = nil
end end
end end
@ -79,7 +81,7 @@ public
super() super()
@client = Client.new(nil, "SOAP4R/#{ Version }") @client = Client.new(nil, "SOAP4R/#{ Version }")
@wiredump_file_base = nil @wiredump_file_base = nil
@charset = @wiredump_dev = @nil @charset = @wiredump_dev = nil
@options = options @options = options
set_options set_options
@client.debug_dev = @wiredump_dev @client.debug_dev = @wiredump_dev
@ -100,7 +102,8 @@ public
end end
def send(endpoint_url, conn_data, soapaction = nil, charset = @charset) def send(endpoint_url, conn_data, soapaction = nil, charset = @charset)
send_post(endpoint_url, conn_data, soapaction, charset) conn_data.soapaction ||= soapaction # for backward conpatibility
send_post(endpoint_url, conn_data, charset)
end end
def reset(endpoint_url = nil) def reset(endpoint_url = nil)
@ -115,24 +118,7 @@ public
private private
def set_options def set_options
@client.proxy = @options["proxy"] HTTPConfigLoader.set_options(@client, @options)
@options.add_hook("proxy") do |key, value|
@client.proxy = value
end
@client.no_proxy = @options["no_proxy"]
@options.add_hook("no_proxy") do |key, value|
@client.no_proxy = value
end
if @client.respond_to?(:protocol_version=)
@client.protocol_version = @options["protocol_version"]
@options.add_hook("protocol_version") do |key, value|
@client.protocol_version = value
end
end
set_cookie_store_file(@options["cookie_store_file"])
@options.add_hook("cookie_store_file") do |key, value|
set_cookie_store_file(value)
end
@charset = @options["charset"] || XSD::Charset.charset_label($KCODE) @charset = @options["charset"] || XSD::Charset.charset_label($KCODE)
@options.add_hook("charset") do |key, value| @options.add_hook("charset") do |key, value|
@charset = value @charset = value
@ -142,96 +128,23 @@ private
@wiredump_dev = value @wiredump_dev = value
@client.debug_dev = @wiredump_dev @client.debug_dev = @wiredump_dev
end end
ssl_config = @options["ssl_config"] ||= ::SOAP::Property.new set_cookie_store_file(@options["cookie_store_file"])
set_ssl_config(ssl_config) @options.add_hook("cookie_store_file") do |key, value|
ssl_config.add_hook(true) do |key, value| set_cookie_store_file(value)
set_ssl_config(ssl_config)
end
basic_auth = @options["basic_auth"] ||= ::SOAP::Property.new
set_basic_auth(basic_auth)
basic_auth.add_hook do |key, value|
set_basic_auth(basic_auth)
end
@options.add_hook("connect_timeout") do |key, value|
@client.connect_timeout = value
end
@options.add_hook("send_timeout") do |key, value|
@client.send_timeout = value
end
@options.add_hook("receive_timeout") do |key, value|
@client.receive_timeout = value
end end
ssl_config = @options["ssl_config"]
basic_auth = @options["basic_auth"]
@options.lock(true) @options.lock(true)
ssl_config.unlock ssl_config.unlock
basic_auth.unlock basic_auth.unlock
end end
def set_basic_auth(basic_auth)
basic_auth.values.each do |url, userid, passwd|
@client.set_basic_auth(url, userid, passwd)
end
end
def set_cookie_store_file(value) def set_cookie_store_file(value)
@cookie_store = value @cookie_store = value
@client.set_cookie_store(@cookie_store) if @cookie_store @client.set_cookie_store(@cookie_store) if @cookie_store
end end
def set_ssl_config(ssl_config) def send_post(endpoint_url, conn_data, charset)
ssl_config.each do |key, value|
cfg = @client.ssl_config
case key
when 'client_cert'
cfg.client_cert = cert_from_file(value)
when 'client_key'
cfg.client_key = key_from_file(value)
when 'client_ca'
cfg.client_ca = value
when 'ca_path'
cfg.set_trust_ca(value)
when 'ca_file'
cfg.set_trust_ca(value)
when 'crl'
cfg.set_crl(value)
when 'verify_mode'
cfg.verify_mode = ssl_config_int(value)
when 'verify_depth'
cfg.verify_depth = ssl_config_int(value)
when 'options'
cfg.options = value
when 'ciphers'
cfg.ciphers = value
when 'verify_callback'
cfg.verify_callback = value
when 'cert_store'
cfg.cert_store = value
else
raise ArgumentError.new("unknown ssl_config property #{key}")
end
end
end
def ssl_config_int(value)
if value.nil? or value.empty?
nil
else
begin
Integer(value)
rescue ArgumentError
::SOAP::Property::Util.const_from_name(value)
end
end
end
def cert_from_file(filename)
OpenSSL::X509::Certificate.new(File.open(filename) { |f| f.read })
end
def key_from_file(filename)
OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read })
end
def send_post(endpoint_url, conn_data, soapaction, charset)
conn_data.send_contenttype ||= StreamHandler.create_media_type(charset) conn_data.send_contenttype ||= StreamHandler.create_media_type(charset)
if @wiredump_file_base if @wiredump_file_base
@ -243,7 +156,7 @@ private
extra = {} extra = {}
extra['Content-Type'] = conn_data.send_contenttype extra['Content-Type'] = conn_data.send_contenttype
extra['SOAPAction'] = "\"#{ soapaction }\"" extra['SOAPAction'] = "\"#{ conn_data.soapaction }\""
extra['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip? extra['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip?
send_string = conn_data.send_string send_string = conn_data.send_string
@wiredump_dev << "Wire dump:\n\n" if @wiredump_dev @wiredump_dev << "Wire dump:\n\n" if @wiredump_dev

View File

@ -1,5 +1,5 @@
# SOAP4R - SOAP WSDL driver # SOAP4R - SOAP WSDL driver
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -9,19 +9,11 @@
require 'wsdl/parser' require 'wsdl/parser'
require 'wsdl/importer' require 'wsdl/importer'
require 'xsd/qname' require 'xsd/qname'
require 'soap/element' require 'xsd/codegen/gensupport'
require 'soap/baseData'
require 'soap/streamHandler'
require 'soap/mimemessage'
require 'soap/mapping'
require 'soap/mapping/wsdlencodedregistry' require 'soap/mapping/wsdlencodedregistry'
require 'soap/mapping/wsdlliteralregistry' require 'soap/mapping/wsdlliteralregistry'
require 'soap/rpc/rpc' require 'soap/rpc/driver'
require 'soap/rpc/element' require 'wsdl/soap/methodDefCreator'
require 'soap/rpc/proxy'
require 'soap/processor'
require 'soap/header/handlerset'
require 'xsd/codegen/gensupport'
module SOAP module SOAP
@ -32,35 +24,27 @@ class WSDLDriverFactory
attr_reader :wsdl attr_reader :wsdl
def initialize(wsdl, logdev = nil) def initialize(wsdl)
@logdev = logdev
@wsdl = import(wsdl) @wsdl = import(wsdl)
@methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@wsdl)
end end
def inspect def inspect
"#<#{self.class}:#{@wsdl.name}>" "#<#{self.class}:#{@wsdl.name}>"
end end
def create_rpc_driver(servicename = nil, portname = nil)
port = find_port(servicename, portname)
drv = SOAP::RPC::Driver.new(port.soap_address.location)
init_driver(drv, port)
add_operation(drv, port)
drv
end
# depricated old interface
def create_driver(servicename = nil, portname = nil) def create_driver(servicename = nil, portname = nil)
service = if servicename warn("WSDLDriverFactory#create_driver is depricated. Use create_rpc_driver instead.")
@wsdl.service(XSD::QName.new(@wsdl.targetnamespace, servicename)) port = find_port(servicename, portname)
else
@wsdl.services[0]
end
if service.nil?
raise FactoryError.new("Service #{ servicename } not found in WSDL.")
end
port = if portname
service.ports[XSD::QName.new(@wsdl.targetnamespace, portname)]
else
service.ports[0]
end
if port.nil?
raise FactoryError.new("Port #{ portname } not found in WSDL.")
end
if port.soap_address.nil?
raise FactoryError.new("soap:address element not found in WSDL.")
end
WSDLDriver.new(@wsdl, port, @logdev) WSDLDriver.new(@wsdl, port, @logdev)
end end
@ -69,14 +53,110 @@ class WSDLDriverFactory
private private
def find_port(servicename = nil, portname = nil)
service = port = nil
if servicename
service = @wsdl.service(
XSD::QName.new(@wsdl.targetnamespace, servicename))
else
service = @wsdl.services[0]
end
if service.nil?
raise FactoryError.new("service #{servicename} not found in WSDL")
end
if portname
port = service.ports[XSD::QName.new(@wsdl.targetnamespace, portname)]
else
port = service.ports[0]
end
if port.nil?
raise FactoryError.new("port #{portname} not found in WSDL")
end
if port.soap_address.nil?
raise FactoryError.new("soap:address element not found in WSDL")
end
port
end
def init_driver(drv, port)
wsdl_elements = @wsdl.collect_elements
wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
rpc_decode_typemap = wsdl_types +
@wsdl.soap_rpc_complextypes(port.find_binding)
drv.proxy.mapping_registry =
Mapping::WSDLEncodedRegistry.new(rpc_decode_typemap)
drv.proxy.literal_mapping_registry =
Mapping::WSDLLiteralRegistry.new(wsdl_types, wsdl_elements)
end
def add_operation(drv, port)
port.find_binding.operations.each do |op_bind|
op_name = op_bind.soapoperation_name
soapaction = op_bind.soapaction || ''
orgname = op_name.name
name = XSD::CodeGen::GenSupport.safemethodname(orgname)
param_def = create_param_def(op_bind)
opt = {}
opt[:request_style] = opt[:response_style] = op_bind.soapoperation_style
opt[:request_use] = (op_bind.input.soapbody.use || 'literal').intern
opt[:response_use] = (op_bind.output.soapbody.use || 'literal').intern
if op_bind.soapoperation_style == :rpc
drv.add_rpc_operation(op_name, soapaction, name, param_def, opt)
else
drv.add_document_operation(soapaction, name, param_def, opt)
end
if orgname != name and orgname.capitalize == name.capitalize
::SOAP::Mapping.define_singleton_method(drv, orgname) do |*arg|
__send__(name, *arg)
end
end
end
end
def import(location) def import(location)
WSDL::Importer.import(location) WSDL::Importer.import(location)
end end
def create_param_def(op_bind)
op = op_bind.find_operation
if op_bind.soapoperation_style == :rpc
param_def = @methoddefcreator.collect_rpcparameter(op)
else
param_def = @methoddefcreator.collect_documentparameter(op)
end
# the first element of typedef in param_def is a String like
# "::SOAP::SOAPStruct". turn this String to a class.
param_def.collect { |io, typedef, name|
typedef[0] = Mapping.class_from_name(typedef[0])
[io, name, typedef]
}
end
def partqname(part)
if part.type
part.type
else
part.element
end
end
def param_def(type, name, klass, partqname)
[type, name, [klass, partqname.namespace, partqname.name]]
end
def filter_parts(partsdef, partssource)
parts = partsdef.split(/\s+/)
partssource.find_all { |part| parts.include?(part.name) }
end
end end
class WSDLDriver class WSDLDriver
class << self class << self
def __attr_proxy(symbol, assignable = false)
end
if RUBY_VERSION >= "1.7.0"
def __attr_proxy(symbol, assignable = false) def __attr_proxy(symbol, assignable = false)
name = symbol.to_s name = symbol.to_s
self.__send__(:define_method, name, proc { self.__send__(:define_method, name, proc {
@ -88,6 +168,23 @@ class WSDLDriver
}) })
end end
end end
else
def __attr_proxy(symbol, assignable = false)
name = symbol.to_s
module_eval <<-EOS
def #{name}
@servant.#{name}
end
EOS
if assignable
module_eval <<-EOS
def #{name}=(value)
@servant.#{name} = value
end
EOS
end
end
end
end end
__attr_proxy :options __attr_proxy :options
@ -179,8 +276,10 @@ class WSDLDriver
@wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes @wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
@rpc_decode_typemap = @wsdl_types + @rpc_decode_typemap = @wsdl_types +
@wsdl.soap_rpc_complextypes(port.find_binding) @wsdl.soap_rpc_complextypes(port.find_binding)
@wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(@rpc_decode_typemap) @wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(
@doc_mapper = Mapping::WSDLLiteralRegistry.new(@wsdl_elements, @wsdl_types) @rpc_decode_typemap)
@doc_mapper = Mapping::WSDLLiteralRegistry.new(
@wsdl_types, @wsdl_elements)
endpoint_url = @port.soap_address.location endpoint_url = @port.soap_address.location
# Convert a map which key is QName, to a Hash which key is String. # Convert a map which key is QName, to a Hash which key is String.
@operation = {} @operation = {}
@ -222,15 +321,16 @@ class WSDLDriver
def rpc_call(name, *values) def rpc_call(name, *values)
set_wiredump_file_base(name) set_wiredump_file_base(name)
unless op_info = @operation[name] unless op_info = @operation[name]
raise MethodDefinitionError, "Method: #{name} not defined." raise RuntimeError, "method: #{name} not defined"
end end
req_header = create_request_header req_header = create_request_header
req_body = create_request_body(op_info, *values) req_body = create_request_body(op_info, *values)
opt = create_options({ reqopt = create_options({
:soapaction => op_info.soapaction || @soapaction, :soapaction => op_info.soapaction || @soapaction})
resopt = create_options({
:decode_typemap => @rpc_decode_typemap}) :decode_typemap => @rpc_decode_typemap})
env = @proxy.invoke(req_header, req_body, opt) env = @proxy.route(req_header, req_body, reqopt, resopt)
raise EmptyResponseError.new("Empty response.") unless env raise EmptyResponseError unless env
receive_headers(env.header) receive_headers(env.header)
begin begin
@proxy.check_fault(env.body) @proxy.check_fault(env.body)
@ -249,21 +349,6 @@ class WSDLDriver
end end
end end
def document_call(name, param)
set_wiredump_file_base(name)
op_info = @operation[name]
req_header = header_from_obj(header_obj, op_info)
req_body = body_from_obj(body_obj, op_info)
env = @proxy.invoke(req_header, req_body, op_info.soapaction || @soapaction, @wsdl_types)
raise EmptyResponseError.new("Empty response.") unless env
if env.body.fault
raise ::SOAP::FaultError.new(env.body.fault)
end
res_body_obj = env.body.response ?
Mapping.soap2obj(env.body.response, @mapping_registry) : nil
return env.header, res_body_obj
end
# req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...] # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...]
# req_body: SOAPBasetype/SOAPCompoundtype # req_body: SOAPBasetype/SOAPCompoundtype
def document_send(name, header_obj, body_obj) def document_send(name, header_obj, body_obj)
@ -275,7 +360,7 @@ class WSDLDriver
:soapaction => op_info.soapaction || @soapaction, :soapaction => op_info.soapaction || @soapaction,
:decode_typemap => @wsdl_types}) :decode_typemap => @wsdl_types})
env = @proxy.invoke(req_header, req_body, opt) env = @proxy.invoke(req_header, req_body, opt)
raise EmptyResponseError.new("Empty response.") unless env raise EmptyResponseError unless env
if env.body.fault if env.body.fault
raise ::SOAP::FaultError.new(env.body.fault) raise ::SOAP::FaultError.new(env.body.fault)
end end
@ -297,7 +382,7 @@ class WSDLDriver
def set_wiredump_file_base(name) def set_wiredump_file_base(name)
if @wiredump_file_base if @wiredump_file_base
@proxy.set_wiredump_file_base(@wiredump_file_base + "_#{ name }") @proxy.set_wiredump_file_base(@wiredump_file_base + "_#{name}")
end end
end end
@ -326,7 +411,7 @@ class WSDLDriver
def create_method_struct(op_info, *params) def create_method_struct(op_info, *params)
parts_names = op_info.bodyparts.collect { |part| part.name } parts_names = op_info.bodyparts.collect { |part| part.name }
obj = create_method_obj(parts_names, params) obj = create_method_obj(parts_names, params)
method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.optype_name) method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.op_name)
if method.members.size != parts_names.size if method.members.size != parts_names.size
new_method = SOAPStruct.new new_method = SOAPStruct.new
method.each do |key, value| method.each do |key, value|
@ -356,7 +441,7 @@ class WSDLDriver
if obj.nil? if obj.nil?
nil nil
else else
raise RuntimeError.new("No header definition in schema.") raise RuntimeError.new("no header definition in schema: #{obj}")
end end
elsif op_info.headerparts.size == 1 elsif op_info.headerparts.size == 1
part = op_info.headerparts[0] part = op_info.headerparts[0]
@ -366,7 +451,7 @@ class WSDLDriver
else else
header = SOAPHeader.new() header = SOAPHeader.new()
op_info.headerparts.each do |part| op_info.headerparts.each do |part|
child = Mapping.find_attribute(obj, part.name) child = Mapping.get_attribute(obj, part.name)
ele = headeritem_from_obj(child, part.element || part.eletype) ele = headeritem_from_obj(child, part.element || part.eletype)
header.add(part.name, ele) header.add(part.name, ele)
end end
@ -391,7 +476,7 @@ class WSDLDriver
if obj.nil? if obj.nil?
nil nil
else else
raise RuntimeError.new("No body found in schema.") raise RuntimeError.new("no body found in schema")
end end
elsif op_info.bodyparts.size == 1 elsif op_info.bodyparts.size == 1
part = op_info.bodyparts[0] part = op_info.bodyparts[0]
@ -400,7 +485,7 @@ class WSDLDriver
else else
body = SOAPBody.new body = SOAPBody.new
op_info.bodyparts.each do |part| op_info.bodyparts.each do |part|
child = Mapping.find_attribute(obj, part.name) child = Mapping.get_attribute(obj, part.name)
ele = bodyitem_from_obj(child, part.element || part.type) ele = bodyitem_from_obj(child, part.element || part.type)
body.add(ele.elename.name, ele) body.add(ele.elename.name, ele)
end end
@ -419,39 +504,40 @@ class WSDLDriver
end end
def add_method_interface(op_info) def add_method_interface(op_info)
name = ::XSD::CodeGen::GenSupport.safemethodname(op_info.op_name.name) name = XSD::CodeGen::GenSupport.safemethodname(op_info.op_name.name)
orgname = op_info.op_name.name
parts_names = op_info.bodyparts.collect { |part| part.name }
case op_info.style case op_info.style
when :document when :document
add_document_method_interface(name) if orgname != name and orgname.capitalize == name.capitalize
add_document_method_interface(orgname, parts_names)
end
add_document_method_interface(name, parts_names)
when :rpc when :rpc
parts_names = op_info.bodyparts.collect { |part| part.name }
orgname = op_info.op_name.name
if orgname != name and orgname.capitalize == name.capitalize if orgname != name and orgname.capitalize == name.capitalize
add_rpc_method_interface(orgname, parts_names) add_rpc_method_interface(orgname, parts_names)
end end
add_rpc_method_interface(name, parts_names) add_rpc_method_interface(name, parts_names)
else else
raise RuntimeError.new("Unknown style: #{op_info.style}") raise RuntimeError.new("unknown style: #{op_info.style}")
end end
end end
def add_rpc_method_interface(name, parts_names) def add_rpc_method_interface(name, parts_names)
sclass = class << @host; self; end ::SOAP::Mapping.define_singleton_method(@host, name) do |*arg|
sclass.__send__(:define_method, name, proc { |*arg|
unless arg.size == parts_names.size unless arg.size == parts_names.size
raise ArgumentError.new( raise ArgumentError.new(
"wrong number of arguments (#{arg.size} for #{parts_names.size})") "wrong number of arguments (#{arg.size} for #{parts_names.size})")
end end
@servant.rpc_call(name, *arg) @servant.rpc_call(name, *arg)
}) end
@host.method(name) @host.method(name)
end end
def add_document_method_interface(name) def add_document_method_interface(name, parts_names)
sclass = class << @host; self; end ::SOAP::Mapping.define_singleton_method(@host, name) do |*arg|
sclass.__send__(:define_method, name, proc { |h, b|
@servant.document_send(name, h, b) @servant.document_send(name, h, b)
}) end
@host.method(name) @host.method(name)
end end
@ -476,5 +562,3 @@ end
end end

View File

@ -18,19 +18,16 @@ class Definitions < Info
attr_reader :targetnamespace attr_reader :targetnamespace
attr_reader :imports attr_reader :imports
# Overrides Info#root attr_accessor :location
def root attr_reader :importedschema
@root
end
def root=(root)
@root = root
end
def initialize def initialize
super super
@name = nil @name = nil
@targetnamespace = nil @targetnamespace = nil
@location = nil
@importedschema = {}
@types = nil @types = nil
@imports = [] @imports = []
@messages = XSD::NamedElements.new @messages = XSD::NamedElements.new
@ -53,6 +50,19 @@ class Definitions < Info
end end
end end
def collect_attributes
result = XSD::NamedElements.new
if @types
@types.schemas.each do |schema|
result.concat(schema.collect_attributes)
end
end
@imports.each do |import|
result.concat(import.content.collect_attributes)
end
result
end
def collect_elements def collect_elements
result = XSD::NamedElements.new result = XSD::NamedElements.new
if @types if @types

View File

@ -45,7 +45,15 @@ class Import < Info
end end
@namespace @namespace
when LocationAttrName when LocationAttrName
@location = value.source @location = URI.parse(value.source)
if @location.relative? and !parent.location.nil? and
!parent.location.relative?
@location = parent.location + @location
end
if root.importedschema.key?(@location)
@content = root.importedschema[@location]
else
root.importedschema[@location] = nil # placeholder
@content = import(@location) @content = import(@location)
if @content.is_a?(Definitions) if @content.is_a?(Definitions)
@content.root = root @content.root = root
@ -53,6 +61,8 @@ class Import < Info
@content.targetnamespace = @namespace @content.targetnamespace = @namespace
end end
end end
root.importedschema[@location] = @content
end
@location @location
else else
nil nil
@ -62,7 +72,7 @@ class Import < Info
private private
def import(location) def import(location)
Importer.import(location) Importer.import(location, root)
end end
end end

View File

@ -1,69 +1,37 @@
# WSDL4R - WSDL importer library. # WSDL4R - WSDL importer library.
# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version. # either the dual license version in 2003, or any later version.
require 'wsdl/info' require 'wsdl/xmlSchema/importer'
require 'wsdl/parser' require 'wsdl/parser'
require 'soap/soap'
require 'soap/property'
module WSDL module WSDL
class Importer class Importer < WSDL::XMLSchema::Importer
def self.import(location) def self.import(location, originalroot = nil)
new.import(location) new.import(location, originalroot)
end
def initialize
@web_client = nil
end
def import(location)
STDERR.puts("importing: #{location}") if $DEBUG
content = nil
if FileTest.exist?(location)
content = File.open(location).read
else
client = web_client.new(nil, "WSDL4R")
if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyName)
client.proxy = opt["client.protocol.http.proxy"]
client.no_proxy = opt["client.protocol.http.no_proxy"]
end
client.proxy ||= ::SOAP::Env::HTTP_PROXY
client.no_proxy ||= ::SOAP::Env::NO_PROXY
content = client.get_content(location)
end
opt = {}
begin
WSDL::Parser.new(opt).parse(content)
rescue WSDL::Parser::ParseError => orgexcn
require 'wsdl/xmlSchema/parser'
WSDL::XMLSchema::Parser.new(opt).parse(content)
end
end end
private private
def web_client def parse(content, location, originalroot)
@web_client ||= begin opt = {
require 'http-access2' :location => location,
if HTTPAccess2::VERSION < "2.0" :originalroot => originalroot
raise LoadError.new("http-access/2.0 or later is required.") }
begin
WSDL::Parser.new(opt).parse(content)
rescue WSDL::Parser::ParseError
super(content, location, originalroot)
end end
HTTPAccess2::Client
rescue LoadError
STDERR.puts "Loading http-access2 failed. Net/http is used." if $DEBUG
require 'soap/netHttpClient'
::SOAP::NetHttpClient
end
@web_client
end end
end end

View File

@ -10,16 +10,22 @@ module WSDL
class Info class Info
attr_accessor :root
attr_accessor :parent attr_accessor :parent
attr_accessor :id attr_accessor :id
def initialize def initialize
@root = nil
@parent = nil @parent = nil
@id = nil @id = nil
end end
def root def inspect
@parent.root if self.respond_to?(:name)
sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.name)
else
sprintf("#<%s:0x%x>", self.class.name, __id__)
end
end end
def parse_element(element); end # abstract def parse_element(element); end # abstract

View File

@ -46,21 +46,23 @@ class Operation < Info
end end
def input_info def input_info
op_name = @name typename = input.find_message.name
optype_name = XSD::QName.new(targetnamespace, input.name ? input.name.name : @name.name) NameInfo.new(@name, typename, inputparts)
NameInfo.new(op_name, optype_name, inputparts)
end end
def output_info def output_info
op_name = @name typename = output.find_message.name
optype_name = XSD::QName.new(targetnamespace, output.name ? output.name.name : @name.name) NameInfo.new(@name, typename, outputparts)
NameInfo.new(op_name, optype_name, outputparts)
end end
def inputparts def inputparts
sort_parts(input.find_message.parts) sort_parts(input.find_message.parts)
end end
def inputname
XSD::QName.new(targetnamespace, input.name ? input.name.name : @name.name)
end
def outputparts def outputparts
sort_parts(output.find_message.parts) sort_parts(output.find_message.parts)
end end

View File

@ -37,7 +37,35 @@ class OperationBinding < Info
end end
def find_operation def find_operation
porttype.operations[@name] porttype.operations[@name] or raise RuntimeError.new("#{@name} not found")
end
def soapoperation_name
if @soapoperation
@soapoperation.input_info.op_name
else
find_operation.name
end
end
def soapoperation_style
style = nil
if @soapoperation
style = @soapoperation.operation_style
elsif parent.soapbinding
style = parent.soapbinding.style
else
raise TypeError.new("operation style definition not found")
end
style || :document
end
def soapaction
if @soapoperation
@soapoperation.soapaction
else
nil
end
end end
def parse_element(element) def parse_element(element)

View File

@ -33,7 +33,7 @@ class Param < Info
end end
def find_message def find_message
root.message(@message) root.message(@message) or raise RuntimeError.new("#{@message} not found")
end end
def parse_element(element) def parse_element(element)
@ -61,6 +61,9 @@ class Param < Info
def parse_attr(attr, value) def parse_attr(attr, value)
case attr case attr
when MessageAttrName when MessageAttrName
if value.namespace.nil?
value = XSD::QName.new(targetnamespace, value.source)
end
@message = value @message = value
when NameAttrName when NameAttrName
@name = XSD::QName.new(targetnamespace, value.source) @name = XSD::QName.new(targetnamespace, value.source)

View File

@ -1,5 +1,5 @@
# WSDL4R - WSDL XML Instance parser library. # WSDL4R - WSDL XML Instance parser library.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -53,6 +53,9 @@ public
@parser = XSD::XMLParser.create_parser(self, opt) @parser = XSD::XMLParser.create_parser(self, opt)
@parsestack = nil @parsestack = nil
@lastnode = nil @lastnode = nil
@ignored = {}
@location = opt[:location]
@originalroot = opt[:originalroot]
end end
def parse(string_or_readable) def parse(string_or_readable)
@ -96,7 +99,7 @@ public
def end_element(name) def end_element(name)
lastframe = @parsestack.pop lastframe = @parsestack.pop
unless name == lastframe.name unless name == lastframe.name
raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.") raise UnexpectedElementError.new("closing element name '#{name}' does not match with opening element '#{lastframe.name}'")
end end
decode_tag_end(lastframe.ns, lastframe.node) decode_tag_end(lastframe.ns, lastframe.node)
@lastnode = lastframe.node @lastnode = lastframe.node
@ -106,20 +109,31 @@ private
def decode_tag(ns, name, attrs, parent) def decode_tag(ns, name, attrs, parent)
o = nil o = nil
element = ns.parse(name) elename = ns.parse(name)
if !parent if !parent
if element == DefinitionsName if elename == DefinitionsName
o = Definitions.parse_element(element) o = Definitions.parse_element(elename)
o.location = @location
else else
raise UnknownElementError.new("Unknown element #{ element }.") raise UnknownElementError.new("unknown element: #{elename}")
end end
o.root = @originalroot if @originalroot # o.root = o otherwise
else else
o = parent.parse_element(element) if elename == XMLSchema::AnnotationName
# only the first annotation element is allowed for each xsd element.
o = XMLSchema::Annotation.new
else
o = parent.parse_element(elename)
end
unless o unless o
STDERR.puts("Unknown element #{ element }.") unless @ignored.key?(elename)
warn("ignored element: #{elename}")
@ignored[elename] = elename
end
o = Documentation.new # which accepts any element. o = Documentation.new # which accepts any element.
end end
# node could be a pseudo element. pseudo element has its own parent. # node could be a pseudo element. pseudo element has its own parent.
o.root = parent.root
o.parent = parent if o.parent.nil? o.parent = parent if o.parent.nil?
end end
attrs.each do |key, value| attrs.each do |key, value|
@ -127,7 +141,10 @@ private
value_ele = ns.parse(value, true) value_ele = ns.parse(value, true)
value_ele.source = value # for recovery; value may not be a QName value_ele.source = value # for recovery; value may not be a QName
unless o.parse_attr(attr_ele, value_ele) unless o.parse_attr(attr_ele, value_ele)
STDERR.puts("Unknown attr #{ attr_ele }.") unless @ignored.key?(attr_ele)
warn("ignored attr: #{attr_ele}")
@ignored[attr_ele] = attr_ele
end
end end
end end
o o

View File

@ -33,7 +33,7 @@ class Port < Info
end end
def find_binding def find_binding
root.binding(@binding) root.binding(@binding) or raise RuntimeError.new("#{@binding} not found")
end end
def inputoperation_map def inputoperation_map

View File

@ -28,7 +28,8 @@ class PortType < Info
end end
def find_binding def find_binding
root.bindings.find { |item| item.type == @name } root.bindings.find { |item| item.type == @name } or
raise RuntimeError.new("#{@name} not found")
end end
def locations def locations

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating CGI stub code from WSDL. # WSDL4R - Creating CGI stub code from WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -26,9 +26,7 @@ class CGIStubCreator
end end
def dump(service_name) def dump(service_name)
STDERR.puts "!!! IMPORTANT !!!" warn("CGI stub can have only 1 port. Creating stub for the first port... Rests are ignored.")
STDERR.puts "- CGI stub can only 1 port. Creating stub for the first port... Rests are ignored."
STDERR.puts "!!! IMPORTANT !!!"
port = @definitions.service(service_name).ports[0] port = @definitions.service(service_name).ports[0]
dump_porttype(port.porttype.name) dump_porttype(port.porttype.name)
end end
@ -39,28 +37,28 @@ private
class_name = create_class_name(name) class_name = create_class_name(name)
methoddef, types = MethodDefCreator.new(@definitions).dump(name) methoddef, types = MethodDefCreator.new(@definitions).dump(name)
mr_creator = MappingRegistryCreator.new(@definitions) mr_creator = MappingRegistryCreator.new(@definitions)
c1 = ::XSD::CodeGen::ClassDef.new(class_name) c1 = XSD::CodeGen::ClassDef.new(class_name)
c1.def_require("soap/rpc/cgistub") c1.def_require("soap/rpc/cgistub")
c1.def_require("soap/mapping/registry") c1.def_require("soap/mapping/registry")
c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new")
c1.def_code(mr_creator.dump(types)) c1.def_code(mr_creator.dump(types))
c1.def_code <<-EOD c1.def_code <<-EOD
Methods = [ Methods = [
#{ methoddef.gsub(/^/, " ") } #{methoddef.gsub(/^/, " ")}
] ]
EOD EOD
c2 = ::XSD::CodeGen::ClassDef.new(class_name + "App", c2 = XSD::CodeGen::ClassDef.new(class_name + "App",
"::SOAP::RPC::CGIStub") "::SOAP::RPC::CGIStub")
c2.def_method("initialize", "*arg") do c2.def_method("initialize", "*arg") do
<<-EOD <<-EOD
super(*arg) super(*arg)
servant = #{class_name}.new servant = #{class_name}.new
#{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style| #{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style|
qname = XSD::QName.new(namespace, name_as)
if style == :document if style == :document
@router.add_document_method(servant, qname, soapaction, name, param_def) @router.add_document_operation(servant, soapaction, name, param_def)
else else
@router.add_rpc_method(servant, qname, soapaction, name, param_def) qname = XSD::QName.new(namespace, name_as)
@router.add_rpc_operation(servant, qname, soapaction, name, param_def)
end end
end end
self.mapping_registry = #{class_name}::MappingRegistry self.mapping_registry = #{class_name}::MappingRegistry

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating class definition from WSDL # WSDL4R - Creating class definition from WSDL
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -22,13 +22,16 @@ class ClassDefCreator
@elements = definitions.collect_elements @elements = definitions.collect_elements
@simpletypes = definitions.collect_simpletypes @simpletypes = definitions.collect_simpletypes
@complextypes = definitions.collect_complextypes @complextypes = definitions.collect_complextypes
@faulttypes = definitions.collect_faulttypes if definitions.respond_to?(:collect_faulttypes) @faulttypes = nil
if definitions.respond_to?(:collect_faulttypes)
@faulttypes = definitions.collect_faulttypes
end
end end
def dump(class_name = nil) def dump(type = nil)
result = '' result = "require 'xsd/qname'\n"
if class_name if type
result = dump_classdef(class_name) result = dump_classdef(type.name, type)
else else
str = dump_element str = dump_element
unless str.empty? unless str.empty?
@ -53,117 +56,129 @@ private
def dump_element def dump_element
@elements.collect { |ele| @elements.collect { |ele|
ele.local_complextype ? dump_classdef(ele) : '' if ele.local_complextype
}.join("\n") dump_classdef(ele.name, ele.local_complextype)
elsif ele.local_simpletype
dump_simpletypedef(ele.name, ele.local_simpletype)
else
nil
end
}.compact.join("\n")
end end
def dump_simpletype def dump_simpletype
@simpletypes.collect { |type| @simpletypes.collect { |type|
dump_simpletypedef(type) dump_simpletypedef(type.name, type)
}.join("\n") }.compact.join("\n")
end end
def dump_complextype def dump_complextype
@complextypes.collect { |type| @complextypes.collect { |type|
case type.compoundtype case type.compoundtype
when :TYPE_STRUCT when :TYPE_STRUCT, :TYPE_EMPTY
dump_classdef(type) dump_classdef(type.name, type)
when :TYPE_ARRAY when :TYPE_ARRAY
dump_arraydef(type) dump_arraydef(type)
when :TYPE_SIMPLE when :TYPE_SIMPLE
STDERR.puts("not implemented: ToDo") dump_simpleclassdef(type)
when :TYPE_MAP
# mapped as a general Hash
nil
else else
raise RuntimeError.new( raise RuntimeError.new(
"Unknown kind of complexContent: #{type.compoundtype}") "unknown kind of complexContent: #{type.compoundtype}")
end end
}.join("\n") }.compact.join("\n")
end end
def dump_simpletypedef(simpletype) def dump_simpletypedef(qname, simpletype)
qname = simpletype.name if !simpletype.restriction or simpletype.restriction.enumeration.empty?
if simpletype.restriction.enumeration.empty? return nil
STDERR.puts("#{qname}: simpleType which is not enum type not supported.")
return ''
end end
c = XSD::CodeGen::ModuleDef.new(create_class_name(qname)) c = XSD::CodeGen::ModuleDef.new(create_class_name(qname))
c.comment = "#{ qname.namespace }" c.comment = "#{qname}"
const = {}
simpletype.restriction.enumeration.each do |value| simpletype.restriction.enumeration.each do |value|
c.def_const(safeconstname(value), value.dump) constname = safeconstname(value)
const[constname] ||= 0
if (const[constname] += 1) > 1
constname += "_#{const[constname]}"
end
c.def_const(constname, ndq(value))
end end
c.dump c.dump
end end
def dump_classdef(type_or_element) def dump_simpleclassdef(type_or_element)
qname = type_or_element.name qname = type_or_element.name
base = create_class_name(type_or_element.simplecontent.base)
c = XSD::CodeGen::ClassDef.new(create_class_name(qname), base)
c.comment = "#{qname}"
c.dump
end
def dump_classdef(qname, typedef)
if @faulttypes and @faulttypes.index(qname) if @faulttypes and @faulttypes.index(qname)
c = XSD::CodeGen::ClassDef.new(create_class_name(qname), c = XSD::CodeGen::ClassDef.new(create_class_name(qname),
'::StandardError') '::StandardError')
else else
c = XSD::CodeGen::ClassDef.new(create_class_name(qname)) c = XSD::CodeGen::ClassDef.new(create_class_name(qname))
end end
c.comment = "#{ qname.namespace }" c.comment = "#{qname}"
c.def_classvar('schema_type', qname.name.dump) c.def_classvar('schema_type', ndq(qname.name))
c.def_classvar('schema_ns', qname.namespace.dump) c.def_classvar('schema_ns', ndq(qname.namespace))
schema_attribute = []
schema_element = [] schema_element = []
init_lines = '' init_lines = ''
params = [] params = []
type_or_element.each_element do |element| typedef.each_element do |element|
next unless element.name
name = element.name.name
if element.type == XSD::AnyTypeName if element.type == XSD::AnyTypeName
type = nil type = nil
elsif basetype = basetype_class(element.type) elsif klass = element_basetype(element)
type = basetype.name type = klass.name
else elsif element.type
type = create_class_name(element.type) type = create_class_name(element.type)
else
type = nil # means anyType.
# do we define a class for local complexType from it's name?
# type = create_class_name(element.name)
# <element>
# <complexType>
# <seq...>
# </complexType>
# </element>
end end
name = name_element(element).name
attrname = safemethodname?(name) ? name : safemethodname(name) attrname = safemethodname?(name) ? name : safemethodname(name)
varname = safevarname(name) varname = safevarname(name)
c.def_attr(attrname, true, varname) c.def_attr(attrname, true, varname)
init_lines << "@#{ varname } = #{ varname }\n" init_lines << "@#{varname} = #{varname}\n"
if element.map_as_array? if element.map_as_array?
params << "#{ varname } = []" params << "#{varname} = []"
type << '[]' type << '[]' if type
else else
params << "#{ varname } = nil" params << "#{varname} = nil"
end end
schema_element << [name, type] eleqname = (varname == name) ? nil : element.name
schema_element << [varname, eleqname, type]
end end
unless type_or_element.attributes.empty? unless typedef.attributes.empty?
type_or_element.attributes.each do |attribute| define_attribute(c, typedef.attributes)
name = attribute.name.name init_lines << "@__xmlattr = {}\n"
if basetype = basetype_class(attribute.type)
type = basetype_class(attribute.type).name
else
type = nil
end end
varname = safevarname('attr_' + name)
c.def_method(varname) do <<-__EOD__
@__soap_attribute[#{name.dump}]
__EOD__
end
c.def_method(varname + '=', 'value') do <<-__EOD__
@__soap_attribute[#{name.dump}] = value
__EOD__
end
schema_attribute << [name, type]
end
init_lines << "@__soap_attribute = {}\n"
end
c.def_classvar('schema_attribute',
'{' +
schema_attribute.collect { |name, type|
name.dump + ' => ' + ndq(type)
}.join(', ') +
'}'
)
c.def_classvar('schema_element', c.def_classvar('schema_element',
'{' + '[' +
schema_element.collect { |name, type| schema_element.collect { |varname, name, type|
name.dump + ' => ' + ndq(type) '[' +
(
if name
varname.dump + ', [' + ndq(type) + ', ' + dqname(name) + ']'
else
varname.dump + ', ' + ndq(type)
end
) +
']'
}.join(', ') + }.join(', ') +
'}' ']'
) )
c.def_method('initialize', *params) do c.def_method('initialize', *params) do
init_lines init_lines
@ -171,20 +186,83 @@ private
c.dump c.dump
end end
def element_basetype(ele)
if klass = basetype_class(ele.type)
klass
elsif ele.local_simpletype
basetype_class(ele.local_simpletype.base)
else
nil
end
end
def attribute_basetype(attr)
if klass = basetype_class(attr.type)
klass
elsif attr.local_simpletype
basetype_class(attr.local_simpletype.base)
else
nil
end
end
def basetype_class(type) def basetype_class(type)
if @simpletypes[type] return nil if type.nil?
basetype_mapped_class(@simpletypes[type].base) if simpletype = @simpletypes[type]
basetype_mapped_class(simpletype.base)
else else
basetype_mapped_class(type) basetype_mapped_class(type)
end end
end end
def define_attribute(c, attributes)
schema_attribute = []
attributes.each do |attribute|
name = name_attribute(attribute)
if klass = attribute_basetype(attribute)
type = klass.name
else
type = nil
end
methodname = safemethodname('xmlattr_' + name.name)
c.def_method(methodname) do <<-__EOD__
(@__xmlattr ||= {})[#{dqname(name)}]
__EOD__
end
c.def_method(methodname + '=', 'value') do <<-__EOD__
(@__xmlattr ||= {})[#{dqname(name)}] = value
__EOD__
end
schema_attribute << [name, type]
end
c.def_classvar('schema_attribute',
'{' +
schema_attribute.collect { |name, type|
dqname(name) + ' => ' + ndq(type)
}.join(', ') +
'}'
)
end
def name_element(element)
return element.name if element.name
return element.ref if element.ref
raise RuntimeError.new("cannot define name of #{element}")
end
def name_attribute(attribute)
return attribute.name if attribute.name
return attribute.ref if attribute.ref
raise RuntimeError.new("cannot define name of #{attribute}")
end
def dump_arraydef(complextype) def dump_arraydef(complextype)
qname = complextype.name qname = complextype.name
c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::Array') c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::Array')
c.comment = "#{ qname.namespace }" c.comment = "#{qname}"
c.def_classvar('schema_type', qname.name.dump) type = complextype.child_type
c.def_classvar('schema_ns', qname.namespace.dump) c.def_classvar('schema_type', ndq(type.name))
c.def_classvar('schema_ns', ndq(type.namespace))
c.dump c.dump
end end
end end

View File

@ -21,7 +21,7 @@ module ClassDefCreatorSupport
def create_class_name(qname) def create_class_name(qname)
if klass = basetype_mapped_class(qname) if klass = basetype_mapped_class(qname)
::SOAP::Mapping::DefaultRegistry.find_mapped_obj_class(klass.name) ::SOAP::Mapping::DefaultRegistry.find_mapped_obj_class(klass).name
else else
safeconstname(qname.name) safeconstname(qname.name)
end end
@ -71,6 +71,10 @@ __EOD__
':' + ele ':' + ele
end end
def dqname(qname)
qname.dump
end
private private
def dump_inout_type(param) def dump_inout_type(param)

View File

@ -31,42 +31,44 @@ class ComplexType < Info
else else
:TYPE_STRUCT :TYPE_STRUCT
end end
elsif complexcontent and complexcontent.base == ::SOAP::ValueArrayName elsif complexcontent
if complexcontent.base == ::SOAP::ValueArrayName
:TYPE_ARRAY :TYPE_ARRAY
else
complexcontent.basetype.check_type
end
elsif simplecontent elsif simplecontent
:TYPE_SIMPLE :TYPE_SIMPLE
elsif !attributes.empty? elsif !attributes.empty?
:TYPE_STRUCT :TYPE_STRUCT
else else # empty complexType definition (seen in partner.wsdl of salesforce)
raise NotImplementedError.new("Unknown kind of complexType.") :TYPE_EMPTY
end end
end end
def child_type(name = nil) def child_type(name = nil)
type = nil
case compoundtype case compoundtype
when :TYPE_STRUCT when :TYPE_STRUCT
if ele = find_element(name) if ele = find_element(name)
type = ele.type ele.type
elsif ele = find_element_by_name(name.name) elsif ele = find_element_by_name(name.name)
type = ele.type ele.type
end end
when :TYPE_ARRAY when :TYPE_ARRAY
type = @contenttype ||= content_arytype @contenttype ||= content_arytype
when :TYPE_MAP when :TYPE_MAP
item_ele = find_element_by_name("item") or item_ele = find_element_by_name("item") or
raise RuntimeError.new("'item' element not found in Map definition.") raise RuntimeError.new("'item' element not found in Map definition.")
content = item_ele.local_complextype or content = item_ele.local_complextype or
raise RuntimeError.new("No complexType definition for 'item'.") raise RuntimeError.new("No complexType definition for 'item'.")
if ele = content.find_element(name) if ele = content.find_element(name)
type = ele.type ele.type
elsif ele = content.find_element_by_name(name.name) elsif ele = content.find_element_by_name(name.name)
type = ele.type ele.type
end end
else else
raise NotImplementedError.new("Unknown kind of complexType.") raise NotImplementedError.new("Unknown kind of complexType.")
end end
type
end end
def child_defined_complextype(name) def child_defined_complextype(name)
@ -103,16 +105,21 @@ class ComplexType < Info
return attribute.arytype return attribute.arytype
end end
end end
elsif content.elements.size == 1 and content.elements[0].maxoccurs != '1' if check_array_content(complexcontent.content)
return content.elements[0].type return complexcontent.content.elements[0].type
else
raise RuntimeError.new("Assert: Unknown array definition.")
end end
nil elsif check_array_content(content)
return content.elements[0].type
end
raise RuntimeError.new("Assert: Unknown array definition.")
end end
private private
def check_array_content(content)
content.elements.size == 1 and content.elements[0].maxoccurs != '1'
end
def content_arytype def content_arytype
if arytype = find_arytype if arytype = find_arytype
ns = arytype.namespace ns = arytype.namespace

View File

@ -1,5 +1,5 @@
# WSDL4R - WSDL additional definitions for SOAP. # WSDL4R - WSDL additional definitions for SOAP.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -77,13 +77,13 @@ class Definitions < Info
def collect_faulttypes def collect_faulttypes
result = [] result = []
collect_fault_messages.each do |m| collect_fault_messages.each do |name|
parts = message(m).parts faultparts = message(name).parts
if parts.size != 1 if faultparts.size != 1
raise RuntimeError.new("Expecting fault message to have only 1 part.") raise RuntimeError.new("expecting fault message to have only 1 part")
end end
if result.index(parts[0].type).nil? if result.index(faultparts[0].type).nil?
result << parts[0].type result << faultparts[0].type
end end
end end
result result
@ -111,13 +111,13 @@ private
if op_bind_rpc?(op_bind) if op_bind_rpc?(op_bind)
operation = op_bind.find_operation operation = op_bind.find_operation
if op_bind.input if op_bind.input
type = XMLSchema::ComplexType.new(operation_input_name(operation)) type = XMLSchema::ComplexType.new(op_bind.soapoperation_name)
message = messages[operation.input.message] message = messages[operation.input.message]
type.sequence_elements = elements_from_message(message) type.sequence_elements = elements_from_message(message)
types << type types << type
end end
if op_bind.output if op_bind.output
type = XMLSchema::ComplexType.new(operation_output_name(operation)) type = XMLSchema::ComplexType.new(operation.outputname)
message = messages[operation.output.message] message = messages[operation.output.message]
type.sequence_elements = elements_from_message(message) type.sequence_elements = elements_from_message(message)
types << type types << type
@ -127,23 +127,20 @@ private
types types
end end
def operation_input_name(operation)
operation.input.name || operation.name
end
def operation_output_name(operation)
operation.output.name ||
XSD::QName.new(operation.name.namespace, operation.name.name + "Response")
end
def op_bind_rpc?(op_bind) def op_bind_rpc?(op_bind)
op_bind.soapoperation and op_bind.soapoperation.operation_style == :rpc op_bind.soapoperation_style == :rpc
end end
def elements_from_message(message) def elements_from_message(message)
message.parts.collect { |part| message.parts.collect { |part|
if part.element
collect_elements[part.element]
elsif part.name.nil? or part.type.nil?
raise RuntimeError.new("part of a message must be an element or typed")
else
qname = XSD::QName.new(nil, part.name) qname = XSD::QName.new(nil, part.name)
XMLSchema::Element.new(qname, part.type) XMLSchema::Element.new(qname, part.type)
end
} }
end end
end end

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating driver code from WSDL. # WSDL4R - Creating driver code from WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -46,16 +46,17 @@ private
methoddef, types = MethodDefCreator.new(@definitions).dump(name) methoddef, types = MethodDefCreator.new(@definitions).dump(name)
mr_creator = MappingRegistryCreator.new(@definitions) mr_creator = MappingRegistryCreator.new(@definitions)
binding = @definitions.bindings.find { |item| item.type == name } binding = @definitions.bindings.find { |item| item.type == name }
addresses = @definitions.porttype(name).locations return '' unless binding.soapbinding # not a SOAP binding
address = @definitions.porttype(name).locations[0]
c = ::XSD::CodeGen::ClassDef.new(class_name, "::SOAP::RPC::Driver") c = XSD::CodeGen::ClassDef.new(class_name, "::SOAP::RPC::Driver")
c.def_require("soap/rpc/driver") c.def_require("soap/rpc/driver")
c.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") c.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new")
c.def_const("DefaultEndpointUrl", addresses[0].dump) c.def_const("DefaultEndpointUrl", ndq(address))
c.def_code(mr_creator.dump(types)) c.def_code(mr_creator.dump(types))
c.def_code <<-EOD c.def_code <<-EOD
Methods = [ Methods = [
#{ methoddef.gsub(/^/, " ") } #{methoddef.gsub(/^/, " ")}
] ]
EOD EOD
c.def_method("initialize", "endpoint_url = nil") do c.def_method("initialize", "endpoint_url = nil") do
@ -69,14 +70,19 @@ Methods = [
c.def_privatemethod("init_methods") do c.def_privatemethod("init_methods") do
<<-EOD <<-EOD
Methods.each do |name_as, name, params, soapaction, namespace, style| Methods.each do |name_as, name, params, soapaction, namespace, style|
qname = ::XSD::QName.new(namespace, name_as) qname = XSD::QName.new(namespace, name_as)
if style == :document if style == :document
@proxy.add_document_method(qname, soapaction, name, params) @proxy.add_document_method(soapaction, name, params)
add_document_method_interface(name, name_as) add_document_method_interface(name, params)
else else
@proxy.add_rpc_method(qname, soapaction, name, params) @proxy.add_rpc_method(qname, soapaction, name, params)
add_rpc_method_interface(name, params) add_rpc_method_interface(name, params)
end end
if name_as != name and name_as.capitalize == name.capitalize
::SOAP::Mapping.define_singleton_method(self, name_as) do |*arg|
__send__(name, *arg)
end
end
end end
EOD EOD
end end

View File

@ -21,12 +21,6 @@ class Element < Info
def attributes def attributes
@local_complextype.attributes @local_complextype.attributes
end end
def each_element
@local_complextype.each_element do |element|
yield(element)
end
end
end end

View File

@ -27,6 +27,10 @@ class Fault < Info
@namespace = nil @namespace = nil
end end
def targetnamespace
parent.targetnamespace
end
def parse_element(element) def parse_element(element)
nil nil
end end

View File

@ -32,8 +32,12 @@ class Header < Info
@headerfault = nil @headerfault = nil
end end
def targetnamespace
parent.targetnamespace
end
def find_message def find_message
root.message(@message) root.message(@message) or raise RuntimeError.new("#{@message} not found")
end end
def find_part def find_part
@ -42,7 +46,7 @@ class Header < Info
return part return part
end end
end end
nil raise RuntimeError.new("#{@part} not found")
end end
def parse_element(element) def parse_element(element)
@ -59,7 +63,10 @@ class Header < Info
def parse_attr(attr, value) def parse_attr(attr, value)
case attr case attr
when MessageAttrName when MessageAttrName
@message = XSD::QName.new(targetnamespace, value.source) if value.namespace.nil?
value = XSD::QName.new(targetnamespace, value.source)
end
@message = value
when PartAttrName when PartAttrName
@part = value.source @part = value.source
when UseAttrName when UseAttrName

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating MappingRegistry code from WSDL. # WSDL4R - Creating MappingRegistry code from WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -51,8 +51,10 @@ private
dump_struct_typemap(definedtype) dump_struct_typemap(definedtype)
when :TYPE_ARRAY when :TYPE_ARRAY
dump_array_typemap(definedtype) dump_array_typemap(definedtype)
when :TYPE_MAP, :TYPE_EMPTY
nil
else else
raise NotImplementedError.new("Must not reach here.") raise NotImplementedError.new("must not reach here")
end end
end end
end end
@ -61,10 +63,10 @@ private
ele = definedtype.name ele = definedtype.name
return <<__EOD__ return <<__EOD__
MappingRegistry.set( MappingRegistry.set(
#{ create_class_name(ele) }, #{create_class_name(ele)},
::SOAP::SOAPStruct, ::SOAP::SOAPStruct,
::SOAP::Mapping::Registry::TypedStructFactory, ::SOAP::Mapping::Registry::TypedStructFactory,
{ :type => ::XSD::QName.new("#{ ele.namespace }", "#{ ele.name }") } { :type => #{dqname(ele)} }
) )
__EOD__ __EOD__
end end
@ -76,10 +78,10 @@ __EOD__
@types << type @types << type
return <<__EOD__ return <<__EOD__
MappingRegistry.set( MappingRegistry.set(
#{ create_class_name(ele) }, #{create_class_name(ele)},
::SOAP::SOAPArray, ::SOAP::SOAPArray,
::SOAP::Mapping::Registry::TypedArrayFactory, ::SOAP::Mapping::Registry::TypedArrayFactory,
{ :type => ::XSD::QName.new("#{ type.namespace }", "#{ type.name }") } { :type => #{dqname(type)} }
) )
__EOD__ __EOD__
end end

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating driver code from WSDL. # WSDL4R - Creating driver code from WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -8,6 +8,7 @@
require 'wsdl/info' require 'wsdl/info'
require 'wsdl/soap/classDefCreatorSupport' require 'wsdl/soap/classDefCreatorSupport'
require 'soap/rpc/element'
module WSDL module WSDL
@ -24,110 +25,122 @@ class MethodDefCreator
@simpletypes = @definitions.collect_simpletypes @simpletypes = @definitions.collect_simpletypes
@complextypes = @definitions.collect_complextypes @complextypes = @definitions.collect_complextypes
@elements = @definitions.collect_elements @elements = @definitions.collect_elements
@types = nil @types = []
end end
def dump(porttype) def dump(porttype)
@types = [] @types.clear
result = "" result = ""
operations = @definitions.porttype(porttype).operations operations = @definitions.porttype(porttype).operations
binding = @definitions.porttype_binding(porttype) binding = @definitions.porttype_binding(porttype)
operations.each do |operation| operations.each do |operation|
op_bind = binding.operations[operation.name] op_bind = binding.operations[operation.name]
next unless op_bind # no binding is defined
next unless op_bind.soapoperation # not a SOAP operation binding
result << ",\n" unless result.empty? result << ",\n" unless result.empty?
result << dump_method(operation, op_bind).chomp result << dump_method(operation, op_bind).chomp
end end
return result, @types return result, @types
end end
def collect_rpcparameter(operation)
result = operation.inputparts.collect { |part|
collect_type(part.type)
param_set(::SOAP::RPC::SOAPMethod::IN, rpcdefinedtype(part), part.name)
}
outparts = operation.outputparts
if outparts.size > 0
retval = outparts[0]
collect_type(retval.type)
result << param_set(::SOAP::RPC::SOAPMethod::RETVAL,
rpcdefinedtype(retval), retval.name)
cdr(outparts).each { |part|
collect_type(part.type)
result << param_set(::SOAP::RPC::SOAPMethod::OUT, rpcdefinedtype(part),
part.name)
}
end
result
end
def collect_documentparameter(operation)
param = []
operation.inputparts.each do |input|
param << param_set(::SOAP::RPC::SOAPMethod::IN,
documentdefinedtype(input), input.name)
end
operation.outputparts.each do |output|
param << param_set(::SOAP::RPC::SOAPMethod::OUT,
documentdefinedtype(output), output.name)
end
param
end
private private
def dump_method(operation, binding) def dump_method(operation, binding)
name = safemethodname(operation.name.name) name = safemethodname(operation.name.name)
name_as = operation.name.name name_as = operation.name.name
stylestr = binding.soapoperation.operation_style.id2name style = binding.soapoperation_style
if binding.soapoperation.operation_style == :rpc
soapaction = binding.soapoperation.soapaction
namespace = binding.input.soapbody.namespace namespace = binding.input.soapbody.namespace
params = collect_rpcparameter(operation) if style == :rpc
paramstr = param2str(collect_rpcparameter(operation))
else else
soapaction = namespace = nil paramstr = param2str(collect_documentparameter(operation))
params = collect_documentparameter(operation)
end end
paramstr = param2str(params)
if paramstr.empty? if paramstr.empty?
paramstr = '[]' paramstr = '[]'
else else
paramstr = "[\n" << paramstr.gsub(/^/, ' ') << "\n ]" paramstr = "[\n" << paramstr.gsub(/^/, ' ') << "\n ]"
end end
return <<__EOD__ return <<__EOD__
[#{ dq(name_as) }, #{ dq(name) }, [#{dq(name_as)}, #{dq(name)},
#{ paramstr }, #{paramstr},
#{ ndq(soapaction) }, #{ ndq(namespace) }, #{ sym(stylestr) } #{ndq(binding.soapaction)}, #{ndq(namespace)}, #{sym(style.id2name)}
] ]
__EOD__ __EOD__
end end
def collect_rpcparameter(operation)
result = operation.inputparts.collect { |part|
collect_type(part.type)
param_set('in', rpcdefinedtype(part), part.name)
}
outparts = operation.outputparts
if outparts.size > 0
retval = outparts[0]
collect_type(retval.type)
result << param_set('retval', rpcdefinedtype(retval), retval.name)
cdr(outparts).each { |part|
collect_type(part.type)
result << param_set('out', rpcdefinedtype(part), part.name)
}
end
result
end
def collect_documentparameter(operation)
input = operation.inputparts[0]
output = operation.outputparts[0]
[
param_set('input', documentdefinedtype(input), input.name),
param_set('output', documentdefinedtype(output), output.name)
]
end
def rpcdefinedtype(part) def rpcdefinedtype(part)
if mapped = basetype_mapped_class(part.type) if mapped = basetype_mapped_class(part.type)
['::' + mapped.name] ['::' + mapped.name]
elsif definedtype = @simpletypes[part.type] elsif definedtype = @simpletypes[part.type]
['::' + basetype_mapped_class(definedtype.base).name] ['::' + basetype_mapped_class(definedtype.base).name]
elsif definedtype = @elements[part.element] elsif definedtype = @elements[part.element]
['::SOAP::SOAPStruct', part.element.namespace, part.element.name] #['::SOAP::SOAPStruct', part.element.namespace, part.element.name]
['nil', part.element.namespace, part.element.name]
elsif definedtype = @complextypes[part.type] elsif definedtype = @complextypes[part.type]
case definedtype.compoundtype case definedtype.compoundtype
when :TYPE_STRUCT when :TYPE_STRUCT, :TYPE_EMPTY # ToDo: empty should be treated as void.
['::SOAP::SOAPStruct', part.type.namespace, part.type.name] type = create_class_name(part.type)
[type, part.type.namespace, part.type.name]
when :TYPE_MAP
[Hash.name, part.type.namespace, part.type.name]
when :TYPE_ARRAY when :TYPE_ARRAY
arytype = definedtype.find_arytype || XSD::AnyTypeName arytype = definedtype.find_arytype || XSD::AnyTypeName
ns = arytype.namespace ns = arytype.namespace
name = arytype.name.sub(/\[(?:,)*\]$/, '') name = arytype.name.sub(/\[(?:,)*\]$/, '')
['::SOAP::SOAPArray', ns, name] type = create_class_name(XSD::QName.new(ns, name))
[type + '[]', ns, name]
else else
raise NotImplementedError.new("Must not reach here.") raise NotImplementedError.new("must not reach here")
end end
else else
raise RuntimeError.new("Part: #{part.name} cannot be resolved.") raise RuntimeError.new("part: #{part.name} cannot be resolved")
end end
end end
def documentdefinedtype(part) def documentdefinedtype(part)
if definedtype = @simpletypes[part.type] if mapped = basetype_mapped_class(part.type)
['::' + mapped.name, nil, part.name]
elsif definedtype = @simpletypes[part.type]
['::' + basetype_mapped_class(definedtype.base).name, nil, part.name] ['::' + basetype_mapped_class(definedtype.base).name, nil, part.name]
elsif definedtype = @elements[part.element] elsif definedtype = @elements[part.element]
['::SOAP::SOAPElement', part.element.namespace, part.element.name] ['::SOAP::SOAPElement', part.element.namespace, part.element.name]
elsif definedtype = @complextypes[part.type] elsif definedtype = @complextypes[part.type]
['::SOAP::SOAPElement', part.type.namespace, part.type.name] ['::SOAP::SOAPElement', part.type.namespace, part.type.name]
else else
raise RuntimeError.new("Part: #{part.name} cannot be resolved.") raise RuntimeError.new("part: #{part.name} cannot be resolved")
end end
end end
@ -138,6 +151,7 @@ __EOD__
def collect_type(type) def collect_type(type)
# ignore inline type definition. # ignore inline type definition.
return if type.nil? return if type.nil?
return if @types.include?(type)
@types << type @types << type
return unless @complextypes[type] return unless @complextypes[type]
@complextypes[type].each_element do |element| @complextypes[type].each_element do |element|
@ -147,15 +161,15 @@ __EOD__
def param2str(params) def param2str(params)
params.collect { |param| params.collect { |param|
"[#{ dq(param[0]) }, #{ dq(param[2]) }, #{ type2str(param[1]) }]" "[#{dq(param[0])}, #{dq(param[2])}, #{type2str(param[1])}]"
}.join(",\n") }.join(",\n")
end end
def type2str(type) def type2str(type)
if type.size == 1 if type.size == 1
"[#{ type[0] }]" "[#{dq(type[0])}]"
else else
"[#{ type[0] }, #{ ndq(type[1]) }, #{ dq(type[2]) }]" "[#{dq(type[0])}, #{ndq(type[1])}, #{dq(type[2])}]"
end end
end end

View File

@ -101,8 +101,7 @@ private
"EncodingStyle '#{ soapbody.encodingstyle }' not supported.") "EncodingStyle '#{ soapbody.encodingstyle }' not supported.")
end end
if soapbody.namespace if soapbody.namespace
op_name = op_name.dup op_name = XSD::QName.new(soapbody.namespace, op_name.name)
op_name.namespace = soapbody.namespace
end end
if soapbody.parts if soapbody.parts
target = soapbody.parts.split(/\s+/) target = soapbody.parts.split(/\s+/)
@ -114,8 +113,7 @@ private
end end
faultpart = nil faultpart = nil
soapaction = parent.soapoperation.soapaction OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, parent.soapaction)
OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, soapaction)
end end
end end

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating servant skelton code from WSDL. # WSDL4R - Creating servant skelton code from WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -17,7 +17,7 @@ module SOAP
class ServantSkeltonCreator class ServantSkeltonCreator
include ClassDefCreatorSupport include ClassDefCreatorSupport
include ::XSD::CodeGen::GenSupport include XSD::CodeGen::GenSupport
attr_reader :definitions attr_reader :definitions
@ -42,7 +42,7 @@ private
def dump_porttype(name) def dump_porttype(name)
class_name = create_class_name(name) class_name = create_class_name(name)
c = ::XSD::CodeGen::ClassDef.new(class_name) c = XSD::CodeGen::ClassDef.new(class_name)
operations = @definitions.porttype(name).operations operations = @definitions.porttype(name).operations
operations.each do |operation| operations.each do |operation|
name = safemethodname(operation.name.name) name = safemethodname(operation.name.name)
@ -50,7 +50,7 @@ private
params = input.find_message.parts.collect { |part| params = input.find_message.parts.collect { |part|
safevarname(part.name) safevarname(part.name)
} }
m = ::XSD::CodeGen::MethodDef.new(name, params) do <<-EOD m = XSD::CodeGen::MethodDef.new(name, params) do <<-EOD
p [#{params.join(", ")}] p [#{params.join(", ")}]
raise NotImplementedError.new raise NotImplementedError.new
EOD EOD

View File

@ -1,5 +1,5 @@
# WSDL4R - Creating standalone server stub code from WSDL. # WSDL4R - Creating standalone server stub code from WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -26,10 +26,8 @@ class StandaloneServerStubCreator
end end
def dump(service_name) def dump(service_name)
STDERR.puts "!!! IMPORTANT !!!" warn("- Standalone stub can have only 1 port for now. So creating stub for the first port and rests are ignored.")
STDERR.puts "- Standalone stub can have only 1 port for now. So creating stub for the first port and rests are ignored." warn("- Standalone server stub ignores port location defined in WSDL. Location is http://localhost:10080/ by default. Generated client from WSDL must be configured to point this endpoint manually.")
STDERR.puts "- Standalone server stub ignores port location defined in WSDL. Location is http://localhost:10080/ by default. Generated client from WSDL must be configured to point this endpoint by hand."
STDERR.puts "!!! IMPORTANT !!!"
port = @definitions.service(service_name).ports[0] port = @definitions.service(service_name).ports[0]
dump_porttype(port.porttype.name) dump_porttype(port.porttype.name)
end end
@ -41,28 +39,28 @@ private
methoddef, types = MethodDefCreator.new(@definitions).dump(name) methoddef, types = MethodDefCreator.new(@definitions).dump(name)
mr_creator = MappingRegistryCreator.new(@definitions) mr_creator = MappingRegistryCreator.new(@definitions)
c1 = ::XSD::CodeGen::ClassDef.new(class_name) c1 = XSD::CodeGen::ClassDef.new(class_name)
c1.def_require("soap/rpc/standaloneServer") c1.def_require("soap/rpc/standaloneServer")
c1.def_require("soap/mapping/registry") c1.def_require("soap/mapping/registry")
c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new")
c1.def_code(mr_creator.dump(types)) c1.def_code(mr_creator.dump(types))
c1.def_code <<-EOD c1.def_code <<-EOD
Methods = [ Methods = [
#{ methoddef.gsub(/^/, " ") } #{methoddef.gsub(/^/, " ")}
] ]
EOD EOD
c2 = ::XSD::CodeGen::ClassDef.new(class_name + "App", c2 = XSD::CodeGen::ClassDef.new(class_name + "App",
"::SOAP::RPC::StandaloneServer") "::SOAP::RPC::StandaloneServer")
c2.def_method("initialize", "*arg") do c2.def_method("initialize", "*arg") do
<<-EOD <<-EOD
super(*arg) super(*arg)
servant = #{class_name}.new servant = #{class_name}.new
#{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style| #{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style|
qname = XSD::QName.new(namespace, name_as)
if style == :document if style == :document
@soaplet.app_scope_router.add_document_method(servant, qname, soapaction, name, param_def) @router.add_document_operation(servant, soapaction, name, param_def)
else else
@soaplet.app_scope_router.add_rpc_method(servant, qname, soapaction, name, param_def) qname = XSD::QName.new(namespace, name_as)
@router.add_rpc_operation(servant, qname, soapaction, name, param_def)
end end
end end
self.mapping_registry = #{class_name}::MappingRegistry self.mapping_registry = #{class_name}::MappingRegistry

176
lib/wsdl/soap/wsdl2ruby.rb Normal file
View File

@ -0,0 +1,176 @@
# WSDL4R - WSDL to ruby mapping library.
# Copyright (C) 2002-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'logger'
require 'xsd/qname'
require 'wsdl/importer'
require 'wsdl/soap/classDefCreator'
require 'wsdl/soap/servantSkeltonCreator'
require 'wsdl/soap/driverCreator'
require 'wsdl/soap/clientSkeltonCreator'
require 'wsdl/soap/standaloneServerStubCreator'
require 'wsdl/soap/cgiStubCreator'
module WSDL
module SOAP
class WSDL2Ruby
attr_accessor :location
attr_reader :opt
attr_accessor :logger
attr_accessor :basedir
def run
unless @location
raise RuntimeError, "WSDL location not given"
end
@wsdl = import(@location)
@name = @wsdl.name ? @wsdl.name.name : 'default'
create_file
end
private
def initialize
@location = nil
@opt = {}
@logger = Logger.new(STDERR)
@basedir = nil
@wsdl = nil
@name = nil
end
def create_file
create_classdef if @opt.key?('classdef')
create_servant_skelton(@opt['servant_skelton']) if @opt.key?('servant_skelton')
create_cgi_stub(@opt['cgi_stub']) if @opt.key?('cgi_stub')
create_standalone_server_stub(@opt['standalone_server_stub']) if @opt.key?('standalone_server_stub')
create_driver(@opt['driver']) if @opt.key?('driver')
create_client_skelton(@opt['client_skelton']) if @opt.key?('client_skelton')
end
def create_classdef
@logger.info { "Creating class definition." }
@classdef_filename = @name + '.rb'
check_file(@classdef_filename) or return
write_file(@classdef_filename) do |f|
f << WSDL::SOAP::ClassDefCreator.new(@wsdl).dump
end
end
def create_client_skelton(servicename)
@logger.info { "Creating client skelton." }
servicename ||= @wsdl.services[0].name.name
@client_skelton_filename = servicename + 'Client.rb'
check_file(@client_skelton_filename) or return
write_file(@client_skelton_filename) do |f|
f << shbang << "\n"
f << "require '#{@driver_filename}'\n\n" if @driver_filename
f << WSDL::SOAP::ClientSkeltonCreator.new(@wsdl).dump(
create_name(servicename))
end
end
def create_servant_skelton(porttypename)
@logger.info { "Creating servant skelton." }
@servant_skelton_filename = (porttypename || @name + 'Servant') + '.rb'
check_file(@servant_skelton_filename) or return
write_file(@servant_skelton_filename) do |f|
f << "require '#{@classdef_filename}'\n\n" if @classdef_filename
f << WSDL::SOAP::ServantSkeltonCreator.new(@wsdl).dump(
create_name(porttypename))
end
end
def create_cgi_stub(servicename)
@logger.info { "Creating CGI stub." }
servicename ||= @wsdl.services[0].name.name
@cgi_stubFilename = servicename + '.cgi'
check_file(@cgi_stubFilename) or return
write_file(@cgi_stubFilename) do |f|
f << shbang << "\n"
if @servant_skelton_filename
f << "require '#{@servant_skelton_filename}'\n\n"
end
f << WSDL::SOAP::CGIStubCreator.new(@wsdl).dump(create_name(servicename))
end
end
def create_standalone_server_stub(servicename)
@logger.info { "Creating standalone stub." }
servicename ||= @wsdl.services[0].name.name
@standalone_server_stub_filename = servicename + '.rb'
check_file(@standalone_server_stub_filename) or return
write_file(@standalone_server_stub_filename) do |f|
f << shbang << "\n"
f << "require '#{@servant_skelton_filename}'\n\n" if @servant_skelton_filename
f << WSDL::SOAP::StandaloneServerStubCreator.new(@wsdl).dump(
create_name(servicename))
end
end
def create_driver(porttypename)
@logger.info { "Creating driver." }
@driver_filename = (porttypename || @name) + 'Driver.rb'
check_file(@driver_filename) or return
write_file(@driver_filename) do |f|
f << "require '#{@classdef_filename}'\n\n" if @classdef_filename
f << WSDL::SOAP::DriverCreator.new(@wsdl).dump(
create_name(porttypename))
end
end
def write_file(filename)
if @basedir
filename = File.join(basedir, filename)
end
File.open(filename, "w") do |f|
yield f
end
end
def check_file(filename)
if @basedir
filename = File.join(basedir, filename)
end
if FileTest.exist?(filename)
if @opt.key?('force')
@logger.warn {
"File '#{filename}' exists but overrides it."
}
true
else
@logger.warn {
"File '#{filename}' exists. #{$0} did not override it."
}
false
end
else
@logger.info { "Creates file '#{filename}'." }
true
end
end
def shbang
"#!/usr/bin/env ruby"
end
def create_name(name)
name ? XSD::QName.new(@wsdl.targetnamespace, name) : nil
end
def import(location)
WSDL::Importer.import(location)
end
end
end
end

View File

@ -0,0 +1,34 @@
# WSDL4R - WSDL SOAP documentation element.
# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'wsdl/info'
module WSDL
module XMLSchema
class Annotation < Info
def initialize
super
end
def parse_element(element)
# Accepts any element.
self
end
def parse_attr(attr, value)
# Accepts any attribute.
true
end
end
end
end

View File

@ -1,5 +1,5 @@
# WSDL4R - XMLSchema attribute definition for WSDL. # WSDL4R - XMLSchema attribute definition for WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -14,34 +14,74 @@ module XMLSchema
class Attribute < Info class Attribute < Info
attr_accessor :ref class << self
attr_accessor :use if RUBY_VERSION > "1.7.0"
attr_accessor :form def attr_reader_ref(symbol)
attr_accessor :name name = symbol.to_s
attr_accessor :type self.__send__(:define_method, name, proc {
attr_accessor :default instance_variable_get("@#{name}") ||
attr_accessor :fixed (refelement ? refelement.__send__(name) : nil)
})
end
else
def attr_reader_ref(symbol)
name = symbol.to_s
module_eval <<-EOS
def #{name}
@#{name} || (refelement ? refelement.#{name} : nil)
end
EOS
end
end
end
attr_writer :use
attr_writer :form
attr_writer :name
attr_writer :type
attr_writer :local_simpletype
attr_writer :default
attr_writer :fixed
attr_reader_ref :use
attr_reader_ref :form
attr_reader_ref :name
attr_reader_ref :type
attr_reader_ref :local_simpletype
attr_reader_ref :default
attr_reader_ref :fixed
attr_accessor :ref
attr_accessor :arytype attr_accessor :arytype
def initialize def initialize
super super
@ref = nil
@use = nil @use = nil
@form = nil @form = nil
@name = nil @name = nil
@type = nil @type = nil
@local_simpletype = nil
@default = nil @default = nil
@fixed = nil @fixed = nil
@ref = nil
@refelement = nil
@arytype = nil @arytype = nil
end end
def refelement
@refelement ||= root.collect_attributes[@ref]
end
def targetnamespace def targetnamespace
parent.targetnamespace parent.targetnamespace
end end
def parse_element(element) def parse_element(element)
nil case element
when SimpleTypeName
@local_simpletype = SimpleType.new
@local_simpletype
end
end end
def parse_attr(attr, value) def parse_attr(attr, value)

View File

@ -26,12 +26,17 @@ class ComplexContent < Info
@derivetype = nil @derivetype = nil
@content = nil @content = nil
@attributes = XSD::NamedElements.new @attributes = XSD::NamedElements.new
@basetype = nil
end end
def targetnamespace def targetnamespace
parent.targetnamespace parent.targetnamespace
end end
def basetype
@basetype ||= root.collect_complextypes[@base]
end
def parse_element(element) def parse_element(element)
case element case element
when RestrictionName, ExtensionName when RestrictionName, ExtensionName

View File

@ -1,5 +1,5 @@
# WSDL4R - XMLSchema data definitions. # WSDL4R - XMLSchema data definitions.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -7,10 +7,13 @@
require 'xsd/datatypes' require 'xsd/datatypes'
require 'wsdl/xmlSchema/annotation'
require 'wsdl/xmlSchema/schema' require 'wsdl/xmlSchema/schema'
require 'wsdl/xmlSchema/import' require 'wsdl/xmlSchema/import'
require 'wsdl/xmlSchema/include'
require 'wsdl/xmlSchema/simpleType' require 'wsdl/xmlSchema/simpleType'
require 'wsdl/xmlSchema/simpleRestriction' require 'wsdl/xmlSchema/simpleRestriction'
require 'wsdl/xmlSchema/simpleExtension'
require 'wsdl/xmlSchema/complexType' require 'wsdl/xmlSchema/complexType'
require 'wsdl/xmlSchema/complexContent' require 'wsdl/xmlSchema/complexContent'
require 'wsdl/xmlSchema/simpleContent' require 'wsdl/xmlSchema/simpleContent'
@ -22,12 +25,15 @@ require 'wsdl/xmlSchema/sequence'
require 'wsdl/xmlSchema/attribute' require 'wsdl/xmlSchema/attribute'
require 'wsdl/xmlSchema/unique' require 'wsdl/xmlSchema/unique'
require 'wsdl/xmlSchema/enumeration' require 'wsdl/xmlSchema/enumeration'
require 'wsdl/xmlSchema/length'
require 'wsdl/xmlSchema/pattern'
module WSDL module WSDL
module XMLSchema module XMLSchema
AllName = XSD::QName.new(XSD::Namespace, 'all') AllName = XSD::QName.new(XSD::Namespace, 'all')
AnnotationName = XSD::QName.new(XSD::Namespace, 'annotation')
AnyName = XSD::QName.new(XSD::Namespace, 'any') AnyName = XSD::QName.new(XSD::Namespace, 'any')
AttributeName = XSD::QName.new(XSD::Namespace, 'attribute') AttributeName = XSD::QName.new(XSD::Namespace, 'attribute')
ChoiceName = XSD::QName.new(XSD::Namespace, 'choice') ChoiceName = XSD::QName.new(XSD::Namespace, 'choice')
@ -37,6 +43,9 @@ ElementName = XSD::QName.new(XSD::Namespace, 'element')
EnumerationName = XSD::QName.new(XSD::Namespace, 'enumeration') EnumerationName = XSD::QName.new(XSD::Namespace, 'enumeration')
ExtensionName = XSD::QName.new(XSD::Namespace, 'extension') ExtensionName = XSD::QName.new(XSD::Namespace, 'extension')
ImportName = XSD::QName.new(XSD::Namespace, 'import') ImportName = XSD::QName.new(XSD::Namespace, 'import')
IncludeName = XSD::QName.new(XSD::Namespace, 'include')
LengthName = XSD::QName.new(XSD::Namespace, 'length')
PatternName = XSD::QName.new(XSD::Namespace, 'pattern')
RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction') RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction')
SequenceName = XSD::QName.new(XSD::Namespace, 'sequence') SequenceName = XSD::QName.new(XSD::Namespace, 'sequence')
SchemaName = XSD::QName.new(XSD::Namespace, 'schema') SchemaName = XSD::QName.new(XSD::Namespace, 'schema')

View File

@ -1,5 +1,5 @@
# WSDL4R - XMLSchema element definition for WSDL. # WSDL4R - XMLSchema element definition for WSDL.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -14,23 +14,62 @@ module XMLSchema
class Element < Info class Element < Info
attr_accessor :name # required class << self
attr_accessor :type if RUBY_VERSION > "1.7.0"
attr_accessor :local_complextype def attr_reader_ref(symbol)
attr_accessor :constraint name = symbol.to_s
attr_accessor :maxoccurs self.__send__(:define_method, name, proc {
attr_accessor :minoccurs instance_variable_get("@#{name}") ||
attr_accessor :nillable (refelement ? refelement.__send__(name) : nil)
})
end
else
def attr_reader_ref(symbol)
name = symbol.to_s
module_eval <<-EOS
def #{name}
@#{name} || (refelement ? refelement.#{name} : nil)
end
EOS
end
end
end
def initialize(name = nil, type = XSD::AnyTypeName) attr_writer :name # required
attr_writer :type
attr_writer :local_simpletype
attr_writer :local_complextype
attr_writer :constraint
attr_writer :maxoccurs
attr_writer :minoccurs
attr_writer :nillable
attr_reader_ref :name
attr_reader_ref :type
attr_reader_ref :local_simpletype
attr_reader_ref :local_complextype
attr_reader_ref :constraint
attr_reader_ref :maxoccurs
attr_reader_ref :minoccurs
attr_reader_ref :nillable
attr_accessor :ref
def initialize(name = nil, type = nil)
super() super()
@name = name @name = name
@type = type @type = type
@local_complextype = nil @local_simpletype = @local_complextype = nil
@constraint = nil @constraint = nil
@maxoccurs = '1' @maxoccurs = '1'
@minoccurs = '1' @minoccurs = '1'
@nillable = nil @nillable = nil
@ref = nil
@refelement = nil
end
def refelement
@refelement ||= root.collect_elements[@ref]
end end
def targetnamespace def targetnamespace
@ -44,6 +83,9 @@ class Element < Info
def parse_element(element) def parse_element(element)
case element case element
when SimpleTypeName
@local_simpletype = SimpleType.new
@local_simpletype
when ComplexTypeName when ComplexTypeName
@type = nil @type = nil
@local_complextype = ComplexType.new @local_complextype = ComplexType.new
@ -62,19 +104,19 @@ class Element < Info
@name = XSD::QName.new(targetnamespace, value.source) @name = XSD::QName.new(targetnamespace, value.source)
when TypeAttrName when TypeAttrName
@type = value @type = value
when RefAttrName
@ref = value
when MaxOccursAttrName when MaxOccursAttrName
if parent.is_a?(All) if parent.is_a?(All)
if value.source != '1' if value.source != '1'
raise Parser::AttrConstraintError.new( raise Parser::AttrConstraintError.new("cannot parse #{value} for #{attr}")
"Cannot parse #{ value } for #{ attr }.")
end end
end end
@maxoccurs = value.source @maxoccurs = value.source
when MinOccursAttrName when MinOccursAttrName
if parent.is_a?(All) if parent.is_a?(All)
unless ['0', '1'].include?(value.source) unless ['0', '1'].include?(value.source)
raise Parser::AttrConstraintError.new( raise Parser::AttrConstraintError.new("cannot parse #{value} for #{attr}")
"Cannot parse #{ value } for #{ attr }.")
end end
end end
@minoccurs = value.source @minoccurs = value.source

View File

@ -1,5 +1,5 @@
# WSDL4R - XMLSchema import definition. # WSDL4R - XMLSchema import definition.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -7,6 +7,7 @@
require 'wsdl/info' require 'wsdl/info'
require 'wsdl/xmlSchema/importer'
module WSDL module WSDL
@ -16,11 +17,13 @@ module XMLSchema
class Import < Info class Import < Info
attr_reader :namespace attr_reader :namespace
attr_reader :schemalocation attr_reader :schemalocation
attr_reader :content
def initialize def initialize
super super
@namespace = nil @namespace = nil
@schemalocation = nil @schemalocation = nil
@content = nil
end end
def parse_element(element) def parse_element(element)
@ -32,11 +35,29 @@ class Import < Info
when NamespaceAttrName when NamespaceAttrName
@namespace = value.source @namespace = value.source
when SchemaLocationAttrName when SchemaLocationAttrName
@schemalocation = value.source @schemalocation = URI.parse(value.source)
if @schemalocation.relative? and !parent.location.nil? and
!parent.location.relative?
@schemalocation = parent.location + @schemalocation
end
if root.importedschema.key?(@schemalocation)
@content = root.importedschema[@schemalocation]
else
root.importedschema[@schemalocation] = nil # placeholder
@content = import(@schemalocation)
root.importedschema[@schemalocation] = @content
end
@schemalocation
else else
nil nil
end end
end end
private
def import(location)
Importer.import(location, root)
end
end end

View File

@ -0,0 +1,81 @@
# WSDL4R - XSD importer library.
# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'soap/httpconfigloader'
require 'wsdl/xmlSchema/parser'
module WSDL
module XMLSchema
class Importer
def self.import(location, originalroot = nil)
new.import(location, originalroot)
end
def initialize
@web_client = nil
end
def import(location, originalroot = nil)
unless location.is_a?(URI)
location = URI.parse(location)
end
content = parse(fetch(location), location, originalroot)
content.location = location
content
end
private
def parse(content, location, originalroot)
opt = {
:location => location,
:originalroot => originalroot
}
WSDL::XMLSchema::Parser.new(opt).parse(content)
end
def fetch(location)
warn("importing: #{location}") if $DEBUG
content = nil
if location.scheme == 'file' or
(location.relative? and FileTest.exist?(location.path))
content = File.open(location.path).read
else
client = web_client.new(nil, "WSDL4R")
client.proxy = ::SOAP::Env::HTTP_PROXY
client.no_proxy = ::SOAP::Env::NO_PROXY
if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyName)
::SOAP::HTTPConfigLoader.set_options(client, opt["client.protocol.http"])
end
content = client.get_content(location)
end
content
end
def web_client
@web_client ||= begin
require 'http-access2'
if HTTPAccess2::VERSION < "2.0"
raise LoadError.new("http-access/2.0 or later is required.")
end
HTTPAccess2::Client
rescue LoadError
warn("Loading http-access2 failed. Net/http is used.") if $DEBUG
require 'soap/netHttpClient'
::SOAP::NetHttpClient
end
@web_client
end
end
end
end

View File

@ -0,0 +1,54 @@
# WSDL4R - XMLSchema include definition.
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'wsdl/info'
require 'wsdl/xmlSchema/importer'
module WSDL
module XMLSchema
class Include < Info
attr_reader :schemalocation
attr_reader :content
def initialize
super
@schemalocation = nil
@content = nil
end
def parse_element(element)
nil
end
def parse_attr(attr, value)
case attr
when SchemaLocationAttrName
@schemalocation = URI.parse(value.source)
if @schemalocation.relative?
@schemalocation = parent.location + @schemalocation
end
@content = import(@schemalocation)
@schemalocation
else
nil
end
end
private
def import(location)
Importer.import(location)
end
end
end
end

View File

@ -0,0 +1,35 @@
# WSDL4R - XMLSchema length definition for WSDL.
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'wsdl/info'
module WSDL
module XMLSchema
class Length < Info
def initialize
super
end
def parse_element(element)
nil
end
def parse_attr(attr, value)
case attr
when ValueAttrName
value.source
end
end
end
end
end

View File

@ -1,5 +1,5 @@
# WSDL4R - WSDL XML Instance parser library. # WSDL4R - WSDL XML Instance parser library.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -51,6 +51,9 @@ public
@parser = XSD::XMLParser.create_parser(self, opt) @parser = XSD::XMLParser.create_parser(self, opt)
@parsestack = nil @parsestack = nil
@lastnode = nil @lastnode = nil
@ignored = {}
@location = opt[:location]
@originalroot = opt[:originalroot]
end end
def parse(string_or_readable) def parse(string_or_readable)
@ -94,7 +97,7 @@ public
def end_element(name) def end_element(name)
lastframe = @parsestack.pop lastframe = @parsestack.pop
unless name == lastframe.name unless name == lastframe.name
raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.") raise UnexpectedElementError.new("closing element name '#{name}' does not match with opening element '#{lastframe.name}'")
end end
decode_tag_end(lastframe.ns, lastframe.node) decode_tag_end(lastframe.ns, lastframe.node)
@lastnode = lastframe.node @lastnode = lastframe.node
@ -104,20 +107,31 @@ private
def decode_tag(ns, name, attrs, parent) def decode_tag(ns, name, attrs, parent)
o = nil o = nil
element = ns.parse(name) elename = ns.parse(name)
if !parent if !parent
if element == SchemaName if elename == SchemaName
o = Schema.parse_element(element) o = Schema.parse_element(elename)
o.location = @location
else else
raise UnknownElementError.new("Unknown element #{ element }.") raise UnknownElementError.new("unknown element: #{elename}")
end end
o.root = @originalroot if @originalroot # o.root = o otherwise
else else
o = parent.parse_element(element) if elename == AnnotationName
# only the first annotation element is allowed for each element.
o = Annotation.new
else
o = parent.parse_element(elename)
end
unless o unless o
STDERR.puts("Unknown element #{ element }.") unless @ignored.key?(elename)
warn("ignored element: #{elename} of #{parent.class}")
@ignored[elename] = elename
end
o = Documentation.new # which accepts any element. o = Documentation.new # which accepts any element.
end end
# node could be a pseudo element. pseudo element has its own parent. # node could be a pseudo element. pseudo element has its own parent.
o.root = parent.root
o.parent = parent if o.parent.nil? o.parent = parent if o.parent.nil?
end end
attrs.each do |key, value| attrs.each do |key, value|
@ -128,7 +142,10 @@ private
o.id = value_ele o.id = value_ele
else else
unless o.parse_attr(attr_ele, value_ele) unless o.parse_attr(attr_ele, value_ele)
STDERR.puts("Unknown attr #{ attr_ele }.") unless @ignored.key?(attr_ele)
warn("ignored attr: #{attr_ele}")
@ignored[attr_ele] = attr_ele
end
end end
end end
end end

View File

@ -0,0 +1,36 @@
# WSDL4R - XMLSchema pattern definition for WSDL.
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'wsdl/info'
module WSDL
module XMLSchema
class Pattern < Info
def initialize
super
end
def parse_element(element)
nil
end
def parse_attr(attr, value)
case attr
when ValueAttrName
parent.pattern = /\A#{value.source}\z/n
value.source
end
end
end
end
end

View File

@ -24,6 +24,8 @@ class Schema < Info
attr_accessor :attributeformdefault attr_accessor :attributeformdefault
attr_accessor :elementformdefault attr_accessor :elementformdefault
attr_reader :importedschema
def initialize def initialize
super super
@targetnamespace = nil @targetnamespace = nil
@ -33,6 +35,17 @@ class Schema < Info
@attributes = XSD::NamedElements.new @attributes = XSD::NamedElements.new
@imports = [] @imports = []
@elementformdefault = "qualified" @elementformdefault = "qualified"
@importedschema = {}
@location = nil
@root = self
end
def location
@location || (root.nil? ? nil : root.location)
end
def location=(location)
@location = location
end end
def parse_element(element) def parse_element(element)
@ -41,6 +54,10 @@ class Schema < Info
o = Import.new o = Import.new
@imports << o @imports << o
o o
when IncludeName
o = Include.new
@imports << o
o
when ComplexTypeName when ComplexTypeName
o = ComplexType.new o = ComplexType.new
@complextypes << o @complextypes << o
@ -55,6 +72,7 @@ class Schema < Info
o o
when AttributeName when AttributeName
o = Attribute.new o = Attribute.new
@attributes << o
o o
else else
nil nil
@ -74,21 +92,39 @@ class Schema < Info
end end
end end
def collect_attributes
result = XSD::NamedElements.new
result.concat(@attributes)
@imports.each do |import|
result.concat(import.content.collect_attributes) if import.content
end
result
end
def collect_elements def collect_elements
result = XSD::NamedElements.new result = XSD::NamedElements.new
result.concat(@elements) result.concat(@elements)
@imports.each do |import|
result.concat(import.content.collect_elements) if import.content
end
result result
end end
def collect_complextypes def collect_complextypes
result = XSD::NamedElements.new result = XSD::NamedElements.new
result.concat(@complextypes) result.concat(@complextypes)
@imports.each do |import|
result.concat(import.content.collect_complextypes) if import.content
end
result result
end end
def collect_simpletypes def collect_simpletypes
result = XSD::NamedElements.new result = XSD::NamedElements.new
result.concat(@simpletypes) result.concat(@simpletypes)
@imports.each do |import|
result.concat(import.content.collect_simpletypes) if import.content
end
result result
end end

View File

@ -1,5 +1,5 @@
# WSDL4R - XMLSchema simpleContent definition for WSDL. # WSDL4R - XMLSchema simpleContent definition for WSDL.
# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -15,17 +15,21 @@ module XMLSchema
class SimpleContent < Info class SimpleContent < Info
attr_accessor :base attr_reader :restriction
attr_reader :derivetype attr_reader :extension
attr_reader :content
attr_reader :attributes def check_lexical_format(value)
check(value)
end
def initialize def initialize
super super
@base = nil @restriction = nil
@derivetype = nil @extension = nil
@content = nil end
@attributes = XSD::NamedElements.new
def base
content.base
end end
def targetnamespace def targetnamespace
@ -34,28 +38,24 @@ class SimpleContent < Info
def parse_element(element) def parse_element(element)
case element case element
when RestrictionName, ExtensionName when RestrictionName
@derivetype = element.name @restriction = SimpleRestriction.new
self @restriction
when AttributeName when ExtensionName
if @derivetype.nil? @extension = SimpleExtension.new
raise Parser::ElementConstraintError.new("base attr not found.") @extension
end
o = Attribute.new
@attributes << o
o
end end
end end
def parse_attr(attr, value) private
if @derivetype.nil?
return nil def content
@restriction || @extension
end end
case attr
when BaseAttrName def check(value)
@base = value unless content.valid?(value)
else raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'")
nil
end end
end end
end end

View File

@ -0,0 +1,54 @@
# WSDL4R - XMLSchema simpleType extension definition for WSDL.
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'wsdl/info'
require 'xsd/namedelements'
module WSDL
module XMLSchema
class SimpleExtension < Info
attr_reader :base
attr_reader :attributes
def initialize
super
@base = nil
@attributes = XSD::NamedElements.new
end
def targetnamespace
parent.targetnamespace
end
def valid?(value)
true
end
def parse_element(element)
case element
when AttributeName
o = Attribute.new
@attributes << o
o
end
end
def parse_attr(attr, value)
case attr
when BaseAttrName
@base = value
end
end
end
end
end

View File

@ -1,4 +1,4 @@
# WSDL4R - XMLSchema simpleType definition for WSDL. # WSDL4R - XMLSchema simpleContent restriction definition for WSDL.
# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
@ -17,21 +17,32 @@ module XMLSchema
class SimpleRestriction < Info class SimpleRestriction < Info
attr_reader :base attr_reader :base
attr_reader :enumeration attr_reader :enumeration
attr_accessor :length
attr_accessor :pattern
def initialize def initialize
super super
@base = nil @base = nil
@enumeration = [] # NamedElements? @enumeration = [] # NamedElements?
@length = nil
@pattern = nil
end end
def valid?(value) def valid?(value)
@enumeration.include?(value) return false unless check_restriction(value)
return false unless check_length(value)
return false unless check_pattern(value)
true
end end
def parse_element(element) def parse_element(element)
case element case element
when EnumerationName when EnumerationName
Enumeration.new # just a parsing handler Enumeration.new # just a parsing handler
when LengthName
Length.new # just a parsing handler
when PatternName
Pattern.new # just a parsing handler
end end
end end
@ -41,6 +52,20 @@ class SimpleRestriction < Info
@base = value @base = value
end end
end end
private
def check_restriction(value)
@enumeration.empty? or @enumeration.include?(value)
end
def check_length(value)
@length.nil? or value.size == @length
end
def check_pattern(value)
@pattern.nil? or @pattern =~ value
end
end end

View File

@ -1,5 +1,5 @@
# WSDL4R - XMLSchema simpleType definition for WSDL. # WSDL4R - XMLSchema simpleType definition for WSDL.
# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -16,15 +16,11 @@ module XMLSchema
class SimpleType < Info class SimpleType < Info
attr_accessor :name attr_accessor :name
attr_reader :derivetype
attr_reader :restriction attr_reader :restriction
def check_lexical_format(value) def check_lexical_format(value)
if @restriction if @restriction
check_restriction(value) check_restriction(value)
elsif @extension
raise NotImplementedError
# ToDo
else else
raise ArgumentError.new("incomplete simpleType") raise ArgumentError.new("incomplete simpleType")
end end
@ -33,8 +29,6 @@ class SimpleType < Info
def base def base
if @restriction if @restriction
@restriction.base @restriction.base
elsif @extension
@extension.base
else else
raise ArgumentError.new("incomplete simpleType") raise ArgumentError.new("incomplete simpleType")
end end
@ -43,7 +37,6 @@ class SimpleType < Info
def initialize(name = nil) def initialize(name = nil)
super() super()
@name = name @name = name
@derivetype = nil
@restriction = nil @restriction = nil
end end
@ -55,7 +48,6 @@ class SimpleType < Info
case element case element
when RestrictionName when RestrictionName
@restriction = SimpleRestriction.new @restriction = SimpleRestriction.new
@derivetype = element.name
@restriction @restriction
end end
end end
@ -71,7 +63,7 @@ private
def check_restriction(value) def check_restriction(value)
unless @restriction.valid?(value) unless @restriction.valid?(value)
raise ::XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'.") raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'")
end end
end end
end end

View File

@ -0,0 +1,107 @@
# XSD4R - XSD to ruby mapping library.
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require 'xsd/codegen/gensupport'
require 'wsdl/xmlSchema/importer'
require 'wsdl/soap/classDefCreator'
module WSDL
module XMLSchema
class XSD2Ruby
attr_accessor :location
attr_reader :opt
attr_accessor :logger
attr_accessor :basedir
def run
unless @location
raise RuntimeError, "XML Schema location not given"
end
@xsd = import(@location)
@name = create_classname(@xsd)
create_file
end
private
def initialize
@location = nil
@opt = {}
@logger = Logger.new(STDERR)
@basedir = nil
@xsd = nil
@name = nil
end
def create_file
create_classdef
end
def create_classdef
@logger.info { "Creating class definition." }
@classdef_filename = @name + '.rb'
check_file(@classdef_filename) or return
write_file(@classdef_filename) do |f|
f << WSDL::SOAP::ClassDefCreator.new(@xsd).dump
end
end
def write_file(filename)
if @basedir
filename = File.join(basedir, filename)
end
File.open(filename, "w") do |f|
yield f
end
end
def check_file(filename)
if @basedir
filename = File.join(basedir, filename)
end
if FileTest.exist?(filename)
if @opt.key?('force')
@logger.warn {
"File '#{filename}' exists but overrides it."
}
true
else
@logger.warn {
"File '#{filename}' exists. #{$0} did not override it."
}
false
end
else
@logger.info { "Creates file '#{filename}'." }
true
end
end
def create_classname(xsd)
name = nil
if xsd.targetnamespace
name = xsd.targetnamespace.scan(/[a-zA-Z0-9]+$/)[0]
end
if name.nil?
'default'
else
XSD::CodeGen::GenSupport.safevarname(name)
end
end
def import(location)
WSDL::XMLSchema::Importer.import(location)
end
end
end
end

View File

@ -71,7 +71,7 @@ public
end end
def Charset.encoding=(encoding) def Charset.encoding=(encoding)
STDERR.puts("xsd charset is set to #{encoding}") if $DEBUG warn("xsd charset is set to #{encoding}") if $DEBUG
@encoding = encoding @encoding = encoding
end end

View File

@ -77,7 +77,7 @@ class ClassDef < ModuleDef
end end
buf << dump_class_def_end buf << dump_class_def_end
buf << dump_package_def_end(package) unless package.empty? buf << dump_package_def_end(package) unless package.empty?
buf buf.gsub(/^\s+$/, '')
end end
private private

View File

@ -21,10 +21,10 @@ module CommentDef
private private
def dump_comment def dump_comment
if /^#/ =~ @comment if /\A#/ =~ @comment
format(@comment) format(@comment)
else else
format(@comment).gsub(/^/, "# ") format(@comment).gsub(/^/, '# ')
end end
end end
end end

View File

@ -9,6 +9,49 @@
module XSD module XSD
module CodeGen module CodeGen
# from the file 'keywords' in 1.9.
KEYWORD = %w(
__LINE__
__FILE__
BEGIN
END
alias
and
begin
break
case
class
def
defined?
do
else
elsif
end
ensure
false
for
if
in
module
next
nil
not
or
redo
rescue
retry
return
self
super
then
true
undef
unless
until
when
while
yield
)
module GenSupport module GenSupport
def capitalize(target) def capitalize(target)
@ -25,7 +68,7 @@ module GenSupport
safename = name.scan(/[a-zA-Z0-9_]+/).collect { |ele| safename = name.scan(/[a-zA-Z0-9_]+/).collect { |ele|
GenSupport.capitalize(ele) GenSupport.capitalize(ele)
}.join }.join
unless /^[A-Z]/ =~ safename if /^[A-Z]/ !~ safename or keyword?(safename)
safename = "C_#{safename}" safename = "C_#{safename}"
end end
safename safename
@ -33,12 +76,17 @@ module GenSupport
module_function :safeconstname module_function :safeconstname
def safeconstname?(name) def safeconstname?(name)
/\A[A-Z][a-zA-Z0-9_]*\z/ =~ name /\A[A-Z][a-zA-Z0-9_]*\z/ =~ name and !keyword?(name)
end end
module_function :safeconstname? module_function :safeconstname?
def safemethodname(name) def safemethodname(name)
safevarname(name) safename = name.scan(/[a-zA-Z0-9_]+/).join('_')
safename = uncapitalize(safename)
if /^[a-z]/ !~ safename
safename = "m_#{safename}"
end
safename
end end
module_function :safemethodname module_function :safemethodname
@ -50,18 +98,23 @@ module GenSupport
def safevarname(name) def safevarname(name)
safename = name.scan(/[a-zA-Z0-9_]+/).join('_') safename = name.scan(/[a-zA-Z0-9_]+/).join('_')
safename = uncapitalize(safename) safename = uncapitalize(safename)
unless /^[a-z]/ =~ safename if /^[a-z]/ !~ safename or keyword?(safename)
safename = "m_#{safename}" safename = "v_#{safename}"
end end
safename safename
end end
module_function :safevarname module_function :safevarname
def safevarname?(name) def safevarname?(name)
/\A[a-z_][a-zA-Z0-9_]*\z/ =~ name /\A[a-z_][a-zA-Z0-9_]*\z/ =~ name and !keyword?(name)
end end
module_function :safevarname? module_function :safevarname?
def keyword?(word)
KEYWORD.include?(word)
end
module_function :keyword?
def format(str, indent = nil) def format(str, indent = nil)
str = trim_eol(str) str = trim_eol(str)
str = trim_indent(str) str = trim_indent(str)
@ -76,7 +129,7 @@ private
def trim_eol(str) def trim_eol(str)
str.collect { |line| str.collect { |line|
line.sub(/\r?\n$/, "") + "\n" line.sub(/\r?\n\z/, "") + "\n"
}.join }.join
end end

View File

@ -34,7 +34,7 @@ class MethodDef
buf = "" buf = ""
buf << dump_comment if @comment buf << dump_comment if @comment
buf << dump_method_def buf << dump_method_def
buf << dump_definition if @definition buf << dump_definition if @definition and !@definition.empty?
buf << dump_method_def_end buf << dump_method_def_end
buf buf
end end

View File

@ -89,7 +89,7 @@ class ModuleDef
end end
buf << dump_module_def_end buf << dump_module_def_end
buf << dump_package_def_end(package) unless package.empty? buf << dump_package_def_end(package) unless package.empty?
buf buf.gsub(/^\s+$/, '')
end end
private private

View File

@ -495,6 +495,18 @@ require 'date'
module XSDDateTimeImpl module XSDDateTimeImpl
SecInDay = 86400 # 24 * 60 * 60 SecInDay = 86400 # 24 * 60 * 60
def to_obj(klass)
if klass == Time
to_time
elsif klass == Date
to_date
elsif klass == DateTime
to_datetime
else
nil
end
end
def to_time def to_time
begin begin
if @data.offset * SecInDay == Time.now.utc_offset if @data.offset * SecInDay == Time.now.utc_offset
@ -511,6 +523,14 @@ module XSDDateTimeImpl
end end
end end
def to_date
Date.new0(@data.class.jd_to_ajd(@data.jd, 0, 0), 0, @data.start)
end
def to_datetime
data
end
def tz2of(str) def tz2of(str)
/^(?:Z|(?:([+\-])(\d\d):(\d\d))?)$/ =~ str /^(?:Z|(?:([+\-])(\d\d):(\d\d))?)$/ =~ str
sign = $1 sign = $1
@ -539,9 +559,18 @@ module XSDDateTimeImpl
end end
def screen_data(t) def screen_data(t)
if (t.is_a?(Date)) # convert t to a DateTime as an internal representation.
if t.is_a?(DateTime)
t t
elsif (t.is_a?(Time)) elsif t.is_a?(Date)
if t.respond_to?(:to_datetime) # from 1.9
t.to_datetime
else
t = screen_data_str(t)
t <<= 12 if t.year < 0
t
end
elsif t.is_a?(Time)
sec, min, hour, mday, month, year = t.to_a[0..5] sec, min, hour, mday, month, year = t.to_a[0..5]
diffday = t.usec.to_r / 1000000 / SecInDay diffday = t.usec.to_r / 1000000 / SecInDay
of = t.utc_offset.to_r / SecInDay of = t.utc_offset.to_r / SecInDay

View File

@ -22,7 +22,7 @@ class IconvCharset
out << e.success out << e.success
ch, str = e.failed.split(//, 2) ch, str = e.failed.split(//, 2)
out << '?' out << '?'
STDERR.puts("Failed to convert #{ch}") warn("Failed to convert #{ch}")
retry retry
end end
return out return out

42
lib/xsd/mapping.rb Normal file
View File

@ -0,0 +1,42 @@
# XSD4R - XML Mapping for Ruby
# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
require "soap/parser"
require 'soap/encodingstyle/literalHandler'
require "soap/generator"
require "soap/mapping"
require "soap/mapping/wsdlliteralregistry"
module XSD
module Mapping
MappingRegistry = SOAP::Mapping::WSDLLiteralRegistry.new
MappingOpt = {:default_encodingstyle => SOAP::LiteralNamespace}
def self.obj2xml(obj, elename = nil, io = nil)
if !elename.nil? and !elename.is_a?(XSD::QName)
elename = XSD::QName.new(nil, elename)
end
elename ||= XSD::QName.new(nil, SOAP::Mapping.name2elename(obj.class.to_s))
soap = SOAP::Mapping.obj2soap(obj, MappingRegistry)
soap.elename = elename
generator = SOAP::SOAPGenerator.new(MappingOpt)
generator.generate(soap, io)
end
def self.xml2obj(stream)
parser = SOAP::Parser.new(MappingOpt)
soap = parser.parse(stream)
SOAP::Mapping.soap2obj(soap, MappingRegistry)
end
end
end

View File

@ -1,5 +1,5 @@
# XSD4R - WSDL named element collection. # XSD4R - WSDL named element collection.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -23,6 +23,12 @@ class NamedElements
o o
end end
def freeze
super
@elements.freeze
self
end
def empty? def empty?
size == 0 size == 0
end end
@ -43,6 +49,10 @@ class NamedElements
@elements.find { |item| item.name.name == name } @elements.find { |item| item.name.name == name }
end end
def keys
collect { |element| element.name }
end
def each def each
@elements.each do |element| @elements.each do |element|
yield(element) yield(element)
@ -69,6 +79,8 @@ class NamedElements
self self
end end
Empty = NamedElements.new.freeze
protected protected
def elements=(rhs) def elements=(rhs)

View File

@ -1,5 +1,5 @@
# XSD4R - XML Schema Namespace library # XSD4R - XML Schema Namespace library
# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -20,7 +20,7 @@ class NS
def assign(ns) def assign(ns)
@count += 1 @count += 1
"n#{ @count }" "n#{@count}"
end end
end end
@ -54,7 +54,7 @@ public
end end
def assigned?(ns) def assigned?(ns)
@ns2tag.key?(ns) @default_namespace == ns or @ns2tag.key?(ns)
end end
def assigned_tag?(tag) def assigned_tag?(tag)
@ -74,7 +74,7 @@ public
elsif @ns2tag.key?(name.namespace) elsif @ns2tag.key?(name.namespace)
@ns2tag[name.namespace] + ':' << name.name @ns2tag[name.namespace] + ':' << name.name
else else
raise FormatError.new('Namespace: ' << name.namespace << ' not defined yet.') raise FormatError.new("namespace: #{name.namespace} not defined yet")
end end
end end
@ -83,7 +83,7 @@ public
return true if (name == rhs) return true if (name == rhs)
end end
@tag2ns.each do |assigned_tag, assigned_ns| @tag2ns.each do |assigned_tag, assigned_ns|
if assigned_ns == ns && "#{ assigned_tag }:#{ name }" == rhs if assigned_ns == ns && "#{assigned_tag}:#{name}" == rhs
return true return true
end end
end end
@ -103,22 +103,22 @@ public
end end
# For local attribute key parsing # For local attribute key parsing
# <foo xmlns="urn:" xmlns:n1="urn:" bar="1" n1:baz="2" /> # <foo xmlns="urn:a" xmlns:n1="urn:a" bar="1" n1:baz="2" />
# => # =>
# {}bar, {urn:}baz # {}bar, {urn:a}baz
def parse_local(elem) def parse_local(elem)
ParseRegexp =~ elem ParseRegexp =~ elem
if $2 if $2
ns = @tag2ns[$1] ns = @tag2ns[$1]
name = $2 name = $2
if !ns if !ns
raise FormatError.new('Unknown namespace qualifier: ' << $1) raise FormatError.new("unknown namespace qualifier: #{$1}")
end end
elsif $1 elsif $1
ns = nil ns = nil
name = $1 name = $1
else else
raise FormatError.new("Illegal element format: #{ elem }") raise FormatError.new("illegal element format: #{elem}")
end end
XSD::QName.new(ns, name) XSD::QName.new(ns, name)
end end

View File

@ -24,6 +24,12 @@ class QName
XSD::QName.new(@namespace, name) XSD::QName.new(@namespace, name)
end end
def dump
ns = @namespace.nil? ? 'nil' : @namespace.dump
name = @name.nil? ? 'nil' : @name.dump
"XSD::QName.new(#{ns}, #{name})"
end
def match(rhs) def match(rhs)
unless self.class === rhs unless self.class === rhs
return false return false

View File

@ -45,8 +45,8 @@ end
# Try to load XML processor. # Try to load XML processor.
loaded = false loaded = false
[ [
'xsd/xmlparser/xmlscanner',
'xsd/xmlparser/xmlparser', 'xsd/xmlparser/xmlparser',
'xsd/xmlparser/xmlscanner',
'xsd/xmlparser/rexmlparser', 'xsd/xmlparser/rexmlparser',
].each do |lib| ].each do |lib|
begin begin

View File

@ -1,5 +1,5 @@
# XSD4R - XMLScan XML parser library. # XSD4R - XMLScan XML parser library.
# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license; # redistribute it and/or modify it under the same terms of Ruby's license;
@ -21,12 +21,12 @@ class XMLScanner < XSD::XMLParser::Parser
@attrs = {} @attrs = {}
@curattr = nil @curattr = nil
@scanner = XMLScan::XMLScanner.new(self) @scanner = XMLScan::XMLScanner.new(self)
@scanner.kcode = ::XSD::Charset.charset_str(charset) if charset @scanner.kcode = XSD::Charset.charset_str(charset) if charset
@scanner.parse(string_or_readable) @scanner.parse(string_or_readable)
end end
def scanner_kcode=(charset) def scanner_kcode=(charset)
@scanner.kcode = ::XSD::Charset.charset_str(charset) if charset @scanner.kcode = XSD::Charset.charset_str(charset) if charset
self.xmldecl_encoding = charset self.xmldecl_encoding = charset
end end

View File

@ -1,41 +0,0 @@
class Authmgr
def initialize
@users = {
'NaHi' => 'passwd',
'HiNa' => 'wspass'
}
@sessions = {}
end
def login(userid, passwd)
userid and passwd and @users[userid] == passwd
end
# returns userid
def auth(sessionid)
@sessions[sessionid]
end
def create_session(userid)
while true
key = create_sessionkey
break unless @sessions[key]
end
@sessions[key] = userid
key
end
def get_session(userid)
@sessions.index(userid)
end
def destroy_session(sessionkey)
@sessions.delete(sessionkey)
end
private
def create_sessionkey
Time.now.usec.to_s
end
end

View File

@ -1,40 +0,0 @@
require 'soap/rpc/driver'
require 'soap/header/simplehandler'
server = ARGV.shift || 'http://localhost:7000/'
class ClientAuthHeaderHandler < SOAP::Header::SimpleHandler
MyHeaderName = XSD::QName.new("http://tempuri.org/authHeader", "auth")
def initialize(userid, passwd)
super(MyHeaderName)
@sessionid = nil
@userid = userid
@passwd = passwd
@mustunderstand = true
end
def on_simple_outbound
if @sessionid
{ "sessionid" => @sessionid }
else
{ "userid" => @userid, "passwd" => @passwd }
end
end
def on_simple_inbound(my_header, mustunderstand)
@sessionid = my_header["sessionid"]
end
end
ns = 'http://tempuri.org/authHeaderPort'
serv = SOAP::RPC::Driver.new(server, ns)
serv.add_method('deposit', 'amt')
serv.add_method('withdrawal', 'amt')
serv.headerhandler << ClientAuthHeaderHandler.new('NaHi', 'passwd')
serv.wiredump_dev = STDOUT
p serv.deposit(150)
p serv.withdrawal(120)

View File

@ -1,42 +0,0 @@
require 'soap/rpc/driver'
require 'soap/header/simplehandler'
server = ARGV.shift || 'http://localhost:7000/'
class ClientAuthHeaderHandler < SOAP::Header::SimpleHandler
MyHeaderName = XSD::QName.new("http://tempuri.org/authHeader", "auth")
attr_accessor :sessionid
def initialize
super(MyHeaderName)
@sessionid = nil
end
def on_simple_outbound
if @sessionid
{ "sessionid" => @sessionid }
end
end
def on_simple_inbound(my_header, mustunderstand)
@sessionid = my_header["sessionid"]
end
end
ns = 'http://tempuri.org/authHeaderPort'
serv = SOAP::RPC::Driver.new(server, ns)
serv.add_method('login', 'userid', 'passwd')
serv.add_method('deposit', 'amt')
serv.add_method('withdrawal', 'amt')
h = ClientAuthHeaderHandler.new
serv.headerhandler << h
serv.wiredump_dev = STDOUT
sessionid = serv.login('NaHi', 'passwd')
h.sessionid = sessionid
p serv.deposit(150)
p serv.withdrawal(120)

View File

@ -1,73 +0,0 @@
#!/usr/bin/env ruby
require 'soap/rpc/standaloneServer'
require 'soap/header/simplehandler'
require 'authmgr'
class AuthHeaderPortServer < SOAP::RPC::StandaloneServer
class AuthHeaderService
def self.create
new
end
def deposit(amt)
"deposit #{amt} OK"
end
def withdrawal(amt)
"withdrawal #{amt} OK"
end
end
Name = 'http://tempuri.org/authHeaderPort'
def initialize(*arg)
super
add_rpc_servant(AuthHeaderService.new, Name)
# header handler must be a per request handler.
add_rpc_request_headerhandler(ServerAuthHeaderHandler)
end
class ServerAuthHeaderHandler < SOAP::Header::SimpleHandler
MyHeaderName = XSD::QName.new("http://tempuri.org/authHeader", "auth")
@authmgr = Authmgr.new
def self.create
new(@authmgr)
end
def initialize(authmgr)
super(MyHeaderName)
@authmgr = authmgr
@userid = @sessionid = nil
end
def on_simple_outbound
{ "sessionid" => @sessionid }
end
def on_simple_inbound(my_header, mu)
auth = false
userid = my_header["userid"]
passwd = my_header["passwd"]
if @authmgr.login(userid, passwd)
auth = true
elsif sessionid = my_header["sessionid"]
if userid = @authmgr.auth(sessionid)
@authmgr.destroy_session(sessionid)
auth = true
end
end
raise RuntimeError.new("authentication failed") unless auth
@userid = userid
@sessionid = @authmgr.create_session(userid)
end
end
end
if $0 == __FILE__
svr = AuthHeaderPortServer.new('AuthHeaderPortServer', nil, '0.0.0.0', 7000)
trap(:INT) do
svr.shutdown
end
status = svr.start
end

View File

@ -1,83 +0,0 @@
#!/usr/bin/env ruby
require 'soap/rpc/standaloneServer'
require 'soap/header/simplehandler'
require 'authmgr'
class AuthHeaderPortServer < SOAP::RPC::StandaloneServer
class AuthHeaderService
def initialize(authmgr)
@authmgr = authmgr
end
def login(userid, passwd)
if @authmgr.login(userid, passwd)
@authmgr.create_session(userid)
else
raise RuntimeError.new("authentication failed")
end
end
def deposit(amt)
"deposit #{amt} OK"
end
def withdrawal(amt)
"withdrawal #{amt} OK"
end
end
Name = 'http://tempuri.org/authHeaderPort'
def initialize(*arg)
super
authmgr = Authmgr.new
add_rpc_servant(AuthHeaderService.new(authmgr), Name)
ServerAuthHeaderHandler.init(authmgr)
# header handler must be a per request handler.
add_rpc_request_headerhandler(ServerAuthHeaderHandler)
end
class ServerAuthHeaderHandler < SOAP::Header::SimpleHandler
MyHeaderName = XSD::QName.new("http://tempuri.org/authHeader", "auth")
def self.init(authmgr)
@authmgr = authmgr
end
def self.create
new(@authmgr)
end
def initialize(authmgr)
super(MyHeaderName)
@authmgr = authmgr
@sessionid = nil
end
def on_simple_outbound
if @sessionid
{ "sessionid" => @sessionid }
end
end
def on_simple_inbound(my_header, mu)
auth = false
if sessionid = my_header["sessionid"]
if userid = @authmgr.auth(sessionid)
@authmgr.destroy_session(sessionid)
@sessionid = @authmgr.create_session(userid)
auth = true
end
end
raise RuntimeError.new("authentication failed") unless auth
end
end
end
if $0 == __FILE__
svr = AuthHeaderPortServer.new('AuthHeaderPortServer', nil, '0.0.0.0', 7000)
trap(:INT) do
svr.shutdown
end
status = svr.start
end

View File

@ -1,16 +0,0 @@
#!/usr/bin/env ruby
text = ARGV.shift || 'Hello world.'
lang = ARGV.shift || 'en_fr'
require 'soap/rpc/driver'
server = 'http://services.xmethods.net/perl/soaplite.cgi'
InterfaceNS = 'urn:xmethodsBabelFish'
wireDumpDev = nil # STDERR
drv = SOAP::RPC::Driver.new(server, InterfaceNS)
drv.wiredump_dev = wireDumpDev
drv.add_method_with_soapaction('BabelFish', InterfaceNS + "#BabelFish", 'translationmode', 'sourcedata')
p drv.BabelFish(lang, text)

View File

@ -1,17 +0,0 @@
module CalcService
def self.add(lhs, rhs)
lhs + rhs
end
def self.sub(lhs, rhs)
lhs - rhs
end
def self.multi(lhs, rhs)
lhs * rhs
end
def self.div(lhs, rhs)
lhs / rhs
end
end

View File

@ -1,29 +0,0 @@
class CalcService2
def initialize(value = 0)
@value = value
end
def set(value)
@value = value
end
def get
@value
end
def +(rhs)
@value + rhs
end
def -(rhs)
@value - rhs
end
def *(rhs)
@value * rhs
end
def /(rhs)
@value / rhs
end
end

View File

@ -1,26 +0,0 @@
require 'soap/rpc/driver'
server = ARGV.shift || 'http://localhost:7000/'
# server = 'http://localhost:8808/server.cgi'
calc = SOAP::RPC::Driver.new(server, 'http://tempuri.org/calcService')
#calc.wiredump_dev = STDERR
calc.add_method('add', 'lhs', 'rhs')
calc.add_method('sub', 'lhs', 'rhs')
calc.add_method('multi', 'lhs', 'rhs')
calc.add_method('div', 'lhs', 'rhs')
puts 'add: 1 + 2 # => 3'
puts calc.add(1, 2)
puts 'sub: 1.1 - 2.2 # => -1.1'
puts calc.sub(1.1, 2.2)
puts 'multi: 1.1 * 2.2 # => 2.42'
puts calc.multi(1.1, 2.2)
puts 'div: 5 / 2 # => 2'
puts calc.div(5, 2)
puts 'div: 5.0 / 2 # => 2.5'
puts calc.div(5.0, 2)
puts 'div: 1.1 / 0 # => Infinity'
puts calc.div(1.1, 0)
puts 'div: 1 / 0 # => ZeroDivisionError'
puts calc.div(1, 0)

View File

@ -1,29 +0,0 @@
require 'soap/rpc/driver'
server = ARGV.shift || 'http://localhost:7000/'
# server = 'http://localhost:8808/server2.cgi'
var = SOAP::RPC::Driver.new( server, 'http://tempuri.org/calcService' )
var.add_method( 'set', 'newValue' )
var.add_method( 'get' )
var.add_method_as( '+', 'add', 'rhs' )
var.add_method_as( '-', 'sub', 'rhs' )
var.add_method_as( '*', 'multi', 'rhs' )
var.add_method_as( '/', 'div', 'rhs' )
puts 'var.set( 1 )'
puts '# Bare in mind that another client set another value to this service.'
puts '# This is only a sample for proof of concept.'
var.set( 1 )
puts 'var + 2 # => 1 + 2 = 3'
puts var + 2
puts 'var - 2.2 # => 1 - 2.2 = -1.2'
puts var - 2.2
puts 'var * 2.2 # => 1 * 2.2 = 2.2'
puts var * 2.2
puts 'var / 2 # => 1 / 2 = 0'
puts var / 2
puts 'var / 2.0 # => 1 / 2.0 = 0.5'
puts var / 2.0
puts 'var / 0 # => 1 / 0 => ZeroDivisionError'
puts var / 0

View File

@ -1,20 +0,0 @@
#!/usr/bin/env ruby
require 'webrick'
require 'soap/property'
docroot = "."
port = 8808
if opt = SOAP::Property.loadproperty("samplehttpd.conf")
docroot = opt["docroot"]
port = Integer(opt["port"])
end
s = WEBrick::HTTPServer.new(
:BindAddress => "0.0.0.0",
:Port => port,
:DocumentRoot => docroot,
:CGIPathEnv => ENV['PATH']
)
trap(:INT){ s.shutdown }
s.start

View File

@ -1,2 +0,0 @@
docroot = .
port = 8808

View File

@ -1,15 +0,0 @@
#!/usr/bin/env ruby
require 'soap/rpc/cgistub'
class CalcServer < SOAP::RPC::CGIStub
def initialize(*arg)
super
require 'calc'
servant = CalcService
add_servant(servant, 'http://tempuri.org/calcService')
end
end
status = CalcServer.new('CalcServer', nil).start

View File

@ -1,21 +0,0 @@
#!/usr/bin/env ruby
require 'soap/rpc/standaloneServer'
require 'calc'
class CalcServer < SOAP::RPC::StandaloneServer
def initialize(*arg)
super
servant = CalcService
add_servant(servant, 'http://tempuri.org/calcService')
end
end
if $0 == __FILE__
server = CalcServer.new('CalcServer', nil, '0.0.0.0', 7000)
trap(:INT) do
server.shutdown
end
server.start
end

View File

@ -1,24 +0,0 @@
#!/usr/bin/env ruby
require 'soap/rpc/standaloneServer'
require 'calc2'
class CalcServer2 < SOAP::RPC::StandaloneServer
def on_init
servant = CalcService2.new
add_method(servant, 'set', 'newValue')
add_method(servant, 'get')
add_method_as(servant, '+', 'add', 'lhs')
add_method_as(servant, '-', 'sub', 'lhs')
add_method_as(servant, '*', 'multi', 'lhs')
add_method_as(servant, '/', 'div', 'lhs')
end
end
if $0 == __FILE__
server = CalcServer2.new('CalcServer', 'http://tempuri.org/calcService', '0.0.0.0', 7000)
trap(:INT) do
server.shutdown
end
status = server.start
end

View File

@ -1,43 +0,0 @@
require 'soap/marshal'
class Node; include SOAP::Marshallable
attr_reader :first, :second, :str
def initialize(*init_next)
@first = init_next[0]
@second = init_next[1]
end
end
n9 = Node.new
n81 = Node.new(n9)
n82 = Node.new(n9)
n7 = Node.new(n81, n82)
n61 = Node.new(n7)
n62 = Node.new(n7)
n5 = Node.new(n61, n62)
n41 = Node.new(n5)
n42 = Node.new(n5)
n3 = Node.new(n41, n42)
n21 = Node.new(n3)
n22 = Node.new(n3)
n1 = Node.new(n21, n22)
File.open("digraph_marshalled_string.soap", "wb") do |f|
SOAP::Marshal.dump(n1, f)
end
marshalledString = File.open("digraph_marshalled_string.soap") { |f| f.read }
puts marshalledString
newnode = SOAP::Marshal.unmarshal(marshalledString)
puts newnode.inspect
p newnode.first.first.__id__
p newnode.second.first.__id__
p newnode.first.first.first.first.__id__
p newnode.second.first.second.first.__id__
File.unlink("digraph_marshalled_string.soap")

Some files were not shown because too many files have changed in this diff Show More