1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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/branches/ruby_1_8@8502 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nahi 2005-05-22 13:20:28 +00:00
parent 15b7d43988
commit 991d0c409c
181 changed files with 3183 additions and 9380 deletions

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -56,6 +56,14 @@ module SOAPType
@extraattr = {}
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
node = self
while node = node.parent
@ -399,7 +407,7 @@ public
def to_s()
str = ''
self.each do |key, data|
str << "#{ key }: #{ data }\n"
str << "#{key}: #{data}\n"
end
str
end
@ -442,6 +450,25 @@ public
@array
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
for i in 0..(@array.length - 1)
yield(@array[i], @data[i])
@ -509,6 +536,10 @@ class SOAPElement
@text = text
end
def inspect
sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
end
# Text interface.
attr_accessor :text
alias data text
@ -548,8 +579,19 @@ class SOAPElement
@text
else
hash = {}
proptype = {}
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
hash
end
@ -566,20 +608,41 @@ class SOAPElement
o
end
def self.from_obj(hash_or_string)
def self.from_obj(obj, namespace = nil)
o = SOAPElement.new(nil)
if hash_or_string.is_a?(Hash)
hash_or_string.each do |k, v|
child = self.from_obj(v)
child.elename = k.is_a?(XSD::QName) ? k : XSD::QName.new(nil, k.to_s)
o.add(child)
case obj
when nil
o.text = nil
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)
end
else
child = from_obj(value, namespace)
child.elename = to_elename(elename, namespace)
o.add(child)
end
end
else
o.text = hash_or_string
o.text = obj.to_s
end
o
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
def add_member(name, value)
@ -590,18 +653,32 @@ private
value
end
def add_accessor(name)
methodname = name
if self.respond_to?(methodname)
methodname = safe_accessor_name(methodname)
if RUBY_VERSION > "1.7.0"
def add_accessor(name)
methodname = name
if self.respond_to?(methodname)
methodname = safe_accessor_name(methodname)
end
Mapping.define_singleton_method(self, methodname) do
@data[@array.index(name)]
end
Mapping.define_singleton_method(self, methodname + '=') do |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
sclass = class << self; self; end
sclass.__send__(:define_method, methodname, proc {
@data[@array.index(name)]
})
sclass.__send__(:define_method, methodname + '=', proc { |value|
@data[@array.index(name)] = value
})
end
def safe_accessor_name(name)
@ -624,7 +701,7 @@ public
def initialize(type = nil, rank = 1, arytype = nil)
super()
@type = type || XSD::QName.new
@type = type || ValueArrayName
@rank = rank
@data = Array.new
@sparse = false
@ -646,7 +723,7 @@ public
def [](*idxary)
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
retrieve(idxary)
@ -656,7 +733,8 @@ public
value = idxary.slice!(-1)
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
for i in 0..(idxary.size - 1)
@ -836,7 +914,7 @@ public
private
def self.create_arytype(typename, rank)
"#{ typename }[" << ',' * (rank - 1) << ']'
"#{typename}[" << ',' * (rank - 1) << ']'
end
TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$')

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -99,7 +99,15 @@ class SOAPBody < SOAPStruct
super(nil)
@elename = EleBodyName
@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
end
@ -129,7 +137,7 @@ class SOAPBody < SOAPStruct
end
end
raise SOAPParser::FormatDecodeError.new('No root element.')
raise Parser::FormatDecodeError.new('no root element')
end
end
@ -231,8 +239,7 @@ class SOAPEnvelope < XSD::NSDBase
end
def encode(generator, ns, attrs = {})
SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace,
SOAPNamespaceTag)
SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace, SOAPNamespaceTag)
name = ns.name(@elename)
generator.encode_tag(name, attrs)

View file

@ -1,5 +1,5 @@
# 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
# 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)
attrs = {}
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)
else
data.elename.name
@ -61,8 +61,8 @@ class ASPDotNetHandler < Handler
yield(child, true)
end
else
raise EncodingStyleError.new("Unknown object:#{ data } in this encodingSt
yle.")
raise EncodingStyleError.new(
"unknown object:#{data} in this encodingStyle")
end
end
@ -119,7 +119,6 @@ yle.")
end
def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = ''
o = SOAPUnknown.new(self, elename)
o.parent = parent
@ -190,11 +189,11 @@ yle.")
end
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
# SOAPUnknown does not have parent.
# raise EncodingStyleError.new("Illegal parent: #{ parent }.")
# raise EncodingStyleError.new("illegal parent: #{parent}")
end
end

View file

@ -1,5 +1,5 @@
# 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
# 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)
attrs = {}
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)
else
data.elename.name
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
when SOAPRawString
@ -62,13 +73,14 @@ class LiteralHandler < Handler
yield(child, true)
end
when SOAPElement
generator.encode_tag(name, attrs.update(data.extraattr))
generator.encode_tag(name, attrs)
generator.encode_rawstring(data.text) if data.text
data.each do |key, value|
yield(value, qualified)
end
else
raise EncodingStyleError.new("Unknown object:#{ data } in this encodingStyle.")
raise EncodingStyleError.new(
"unknown object:#{data} in this encodingStyle")
end
end
@ -78,7 +90,8 @@ class LiteralHandler < Handler
else
data.elename.name
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)
end
@ -128,7 +141,6 @@ class LiteralHandler < Handler
end
def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = ''
o = SOAPUnknown.new(self, elename, decode_attrs(ns, attrs))
o.parent = parent
@ -172,21 +184,19 @@ class LiteralHandler < Handler
end
def decode_parent(parent, node)
return unless parent.node
case parent.node
when SOAPUnknown
newparent = parent.node.as_element
node.parent = newparent
parent.replace_node(newparent)
decode_parent(parent, node)
when SOAPElement
parent.node.add(node)
node.parent = parent.node
when SOAPStruct
parent.node.add(node.elename.name, node)
node.parent = parent.node
when SOAPArray
if node.position
parent.node[*(decode_arypos(node.position))] = node
@ -195,13 +205,8 @@ class LiteralHandler < Handler
parent.node.add(node)
end
node.parent = parent.node
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.")
else
# SOAPUnknown does not have parent.
raise EncodingStyleError.new("Illegal parent: #{ parent }.")
raise EncodingStyleError.new("illegal parent: #{parent.node}")
end
end

View file

@ -1,5 +1,5 @@
# 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
# 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)
if parent && parent.is_a?(SOAPArray) && parent.position
attrs[ns.name(AttrPositionName)] = "[#{ parent.position.join(',') }]"
attrs[ns.name(AttrPositionName)] = "[#{parent.position.join(',')}]"
end
name = nil
@ -75,7 +75,7 @@ class SOAPHandler < Handler
end
else
raise EncodingStyleError.new(
"Unknown object:#{ data } in this encodingStyle.")
"unknown object:#{data} in this encodingStyle")
end
end
@ -156,7 +156,6 @@ class SOAPHandler < Handler
end
def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = ''
is_nil, type, arytype, root, offset, position, href, id, extraattr =
decode_attrs(ns, attrs)
@ -227,6 +226,7 @@ class SOAPHandler < Handler
end
def decode_parent(parent, node)
return unless parent.node
case parent.node
when SOAPUnknown
newparent = parent.node.as_struct
@ -247,10 +247,8 @@ class SOAPHandler < Handler
parent.node.add(node)
end
node.parent = parent.node
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.")
else
raise EncodingStyleError.new("Illegal parent: #{ parent.node }.")
raise EncodingStyleError.new("illegal parent: #{parent.node}")
end
end
@ -266,7 +264,7 @@ private
def create_arytype(ns, data)
XSD::QName.new(data.arytype.namespace,
content_typename(data.arytype.name) + "[#{ data.size.join(',') }]")
content_typename(data.arytype.name) + "[#{data.size.join(',')}]")
end
def encode_attrs(generator, ns, data, parent)
@ -383,7 +381,7 @@ private
def decode_definedtype(elename, typename, typedef, arytypestr)
unless typedef
raise EncodingStyleError.new("Unknown type '#{ typename }'.")
raise EncodingStyleError.new("unknown type '#{typename}'")
end
if typedef.is_a?(::WSDL::XMLSchema::SimpleType)
decode_defined_simpletype(elename, typename, typedef, arytypestr)
@ -421,6 +419,10 @@ private
end
o.definedtype = typedef
return o
when :TYPE_EMPTY
o = SOAPNil.decode(elename)
o.definedtype = typedef
return o
else
raise RuntimeError.new(
"Unknown kind of complexType: #{typedef.compoundtype}")
@ -509,7 +511,7 @@ private
case qname.name
when XSD::NilLiteral
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
when XSD::AttrType
type = value
@ -523,7 +525,7 @@ private
when AttrRoot
root = RootLiteralMap[value] or
raise EncodingStyleError.new(
"Illegal root attribute value: #{ value }.")
"illegal root attribute value: #{value}")
next
when AttrOffset
offset = value
@ -578,7 +580,7 @@ private
ref.__setobj__(o)
false
else
raise EncodingStyleError.new("Unresolved reference: #{ ref.refid }.")
raise EncodingStyleError.new("unresolved reference: #{ref.refid}")
end
}
count -= 1

View file

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

View file

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

View file

@ -1,5 +1,5 @@
# 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
# 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
h = on_simple_outbound
h ? SOAPElement.from_obj(h) : nil
h ? SOAPElement.from_obj(h, elename.namespace) : nil
end
def on_inbound(header, mustunderstand)

View file

@ -37,24 +37,28 @@ class Factory
end
def setiv2soap(node, obj, map)
# should we sort instance_variables?
obj.instance_variables.each do |var|
name = var.sub(/^@/, '')
node.add(Mapping.name2elename(name),
Mapping._obj2soap(obj.instance_variable_get(var), 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?
obj.instance_variables.each do |var|
name = var.sub(/^@/, '')
elename = Mapping.name2elename(name)
node.add(elename,
Mapping._obj2soap(obj.instance_variable_get(var), map))
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
private
def setiv2ary(obj, node, map)
@ -68,7 +72,7 @@ private
node.each do |name, value|
vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
end
Mapping.set_instance_vars(obj, vars)
Mapping.set_attributes(obj, vars)
end
end
@ -156,20 +160,14 @@ class DateTimeFactory_ < Factory
end
def soap2obj(obj_class, node, info, map)
obj = nil
if obj_class == Time
obj = node.to_time
if obj.nil?
# Is out of range as a Time
return false
end
elsif obj_class == Date
obj = node.data
if node.respond_to?(:to_obj)
obj = node.to_obj(obj_class)
return false if obj.nil?
mark_unmarshalled_obj(node, obj)
return true, obj
else
return false
end
mark_unmarshalled_obj(node, obj)
return true, obj
end
end

View file

@ -1,5 +1,5 @@
# 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
# 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].
module TraverseSupport
def mark_marshalled_obj(obj, soap_obj)
raise if obj.nil?
Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj
end
def mark_unmarshalled_obj(node, obj)
return if obj.nil?
# node.id is not Object#id but SOAPReference#id
Thread.current[:SOAPMarshalDataKey][node.id] = obj
end
@ -41,10 +43,10 @@ module Mapping
soap_obj
end
def self.soap2obj(node, registry = nil)
def self.soap2obj(node, registry = nil, klass = nil)
registry ||= Mapping::DefaultRegistry
Thread.current[:SOAPMarshalDataKey] = {}
obj = _soap2obj(node, registry)
obj = _soap2obj(node, registry, klass)
Thread.current[:SOAPMarshalDataKey] = nil
obj
end
@ -107,21 +109,21 @@ module Mapping
elsif registry
registry.obj2soap(obj, type)
else
raise MappingError.new("No mapping registry given.")
raise MappingError.new("no mapping registry given")
end
end
def self._soap2obj(node, registry)
def self._soap2obj(node, registry, klass = nil)
if node.is_a?(SOAPReference)
target = node.__getobj__
# target.id is not Object#id but SOAPReference#id
if referent = Thread.current[:SOAPMarshalDataKey][target.id]
return referent
else
return _soap2obj(target, registry)
return _soap2obj(target, registry, klass)
end
end
return registry.soap2obj(node)
return registry.soap2obj(node, klass)
end
if Object.respond_to?(:allocate)
@ -157,29 +159,6 @@ module Mapping
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.
# Caution: '.' is not allowed here.
# To follow XML spec., it should be NCName.
@ -198,28 +177,51 @@ module Mapping
}
end
def self.class_from_name(name)
if /^[A-Z]/ !~ name
def self.const_from_name(name, lenient = false)
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
end
klass = ::Object
name.split('::').each do |klass_str|
if klass.const_defined?(klass_str)
klass = klass.const_get(klass_str)
else
return nil
end
const
end
def self.class_from_name(name, lenient = false)
const = const_from_name(name, lenient)
if const.is_a?(::Class)
const
else
nil
end
end
def self.module_from_name(name, lenient = false)
const = const_from_name(name, lenient)
if const.is_a?(::Module)
const
else
nil
end
klass
end
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')
else
nil
end
namespace = if klass.class_variables.include?("@@schema_ns")
namespace = if klass.class_variables.include?('@@schema_ns')
klass.class_eval('@@schema_ns')
else
nil
@ -250,19 +252,75 @@ module Mapping
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)
obj[attr_name] || obj[attr_name.intern]
else
name = ::XSD::CodeGen::GenSupport.safevarname(attr_name)
if obj.respond_to?(name)
obj.__send__(name)
else
name = XSD::CodeGen::GenSupport.safevarname(attr_name)
if obj.instance_variables.include?('@' + 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
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
private
def add_md_ary(md_ary, ary, indices, registry)

View file

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

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -168,7 +168,7 @@ class RubytypeFactory < Factory
return nil
end
if obj.to_s[0] == ?#
raise TypeError.new("can't dump anonymous class #{ obj }")
raise TypeError.new("can't dump anonymous class #{obj}")
end
param = SOAPStruct.new(TYPE_CLASS)
mark_marshalled_obj(obj, param)
@ -179,7 +179,7 @@ class RubytypeFactory < Factory
return nil
end
if obj.to_s[0] == ?#
raise TypeError.new("can't dump anonymous module #{ obj }")
raise TypeError.new("can't dump anonymous module #{obj}")
end
param = SOAPStruct.new(TYPE_MODULE)
mark_marshalled_obj(obj, param)
@ -222,7 +222,12 @@ class RubytypeFactory < Factory
when ::SOAP::Mapping::Object
param = SOAPStruct.new(XSD::AnyTypeName)
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
typestr = Mapping.name2elename(obj.class.to_s)
param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr))
@ -258,12 +263,12 @@ private
def unknownobj2soap(soap_class, obj, info, map)
if obj.class.name.empty?
raise TypeError.new("can't dump anonymous class #{ obj }")
raise TypeError.new("can't dump anonymous class #{obj}")
end
singleton_class = class << obj; self; end
if !singleton_methods_true(obj).empty? or
!singleton_class.instance_variables.empty?
raise TypeError.new("singleton can't be dumped #{ obj }")
raise TypeError.new("singleton can't be dumped #{obj}")
end
if !(singleton_class.ancestors - obj.class.ancestors).empty?
typestr = Mapping.name2elename(obj.class.to_s)
@ -378,7 +383,11 @@ private
obj = klass.new
mark_unmarshalled_obj(node, obj)
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
return true, obj
else
@ -387,7 +396,12 @@ private
end
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)
return true, obj if obj
if !@allow_untyped_struct
@ -406,6 +420,9 @@ private
end
typestr = Mapping.elename2name(node.type.name)
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?
return nil
end
@ -414,7 +431,13 @@ private
end
klass_type = Mapping.class2qname(klass)
return nil unless node.type.match(klass_type)
obj = Mapping.create_empty_object(klass)
obj = nil
begin
obj = Mapping.create_empty_object(klass)
rescue
# type name "data" tries Data.new which raises TypeError
nil
end
mark_unmarshalled_obj(node, obj)
setiv2obj(obj, node, map)
obj

View file

@ -1,11 +1,13 @@
# 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
# 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/qname'
require 'xsd/namedelements'
require 'soap/baseData'
require 'soap/mapping/mapping'
require 'soap/mapping/typeMap'
@ -15,35 +17,33 @@ module SOAP
module Mapping
class WSDLEncodedRegistry
class WSDLEncodedRegistry < Registry
include TraverseSupport
attr_reader :definedelements
attr_reader :definedtypes
attr_accessor :excn_handler_obj2soap
attr_accessor :excn_handler_soap2obj
def initialize(definedtypes, config = {})
def initialize(definedtypes = XSD::NamedElements::Empty)
@definedtypes = definedtypes
@config = config
# @definedelements = definedelements needed?
@excn_handler_obj2soap = nil
@excn_handler_soap2obj = nil
# For mapping AnyType element.
@rubytype_factory = RubytypeFactory.new(
:allow_untyped_struct => true,
:allow_original_mapping => true
)
@schema_element_cache = {}
end
def obj2soap(obj, type_qname = nil)
def obj2soap(obj, qname = nil)
soap_obj = nil
if obj.nil?
soap_obj = SOAPNil.new
elsif type_qname.nil? or type_qname == XSD::AnyTypeName
soap_obj = @rubytype_factory.obj2soap(nil, obj, nil, self)
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)
if type = @definedtypes[qname]
soap_obj = obj2typesoap(obj, type)
else
soap_obj = any2soap(obj, qname)
end
return soap_obj if soap_obj
if @excn_handler_obj2soap
@ -52,15 +52,46 @@ class WSDLEncodedRegistry
}
return soap_obj if soap_obj
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
def soap2obj(node)
raise RuntimeError.new("#{ self } is for obj2soap only.")
# map anything for now: must refer WSDL while mapping. [ToDo]
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
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)
if obj.is_a?(SOAPBasetype)
obj
@ -82,25 +113,22 @@ private
end
end
def obj2type(obj, type)
def obj2typesoap(obj, type)
if type.is_a?(::WSDL::XMLSchema::SimpleType)
simple2soap(obj, type)
simpleobj2soap(obj, type)
else
complex2soap(obj, type)
complexobj2soap(obj, type)
end
end
def simple2soap(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
def simpleobj2soap(obj, type)
type.check_lexical_format(obj)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
o = base2soap(obj, TypeMap[type.base])
o
end
def complex2soap(obj, type)
def complexobj2soap(obj, type)
case type.compoundtype
when :TYPE_STRUCT
struct2soap(obj, type.name, type)
@ -108,8 +136,13 @@ private
array2soap(obj, type.name, type)
when :TYPE_MAP
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
raise MappingError.new("Unknown compound type: #{ type.compoundtype }")
raise MappingError.new("unknown compound type: #{type.compoundtype}")
end
end
@ -126,18 +159,24 @@ private
end
def struct2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
soap_obj = SOAPStruct.new(type_qname)
mark_marshalled_obj(obj, soap_obj)
elements2soap(obj, soap_obj, type.content.elements)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj)
elements2soap(obj, soap_obj, type.content.elements)
end
soap_obj
end
def array2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
arytype = type.child_type
soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
mark_marshalled_obj(obj, soap_obj)
obj.each do |item|
soap_obj.add(Mapping._obj2soap(item, self, arytype))
unless obj.nil?
mark_marshalled_obj(obj, soap_obj)
obj.each do |item|
soap_obj.add(Mapping._obj2soap(item, self, arytype))
end
end
soap_obj
end
@ -145,16 +184,19 @@ private
MapKeyName = XSD::QName.new(nil, "key")
MapValueName = XSD::QName.new(nil, "value")
def map2soap(obj, type_qname, type)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
keytype = type.child_type(MapKeyName) || XSD::AnyTypeName
valuetype = type.child_type(MapValueName) || XSD::AnyTypeName
soap_obj = SOAPStruct.new(MapQName)
mark_marshalled_obj(obj, soap_obj)
obj.each do |key, value|
elem = SOAPStruct.new
elem.add("key", Mapping._obj2soap(key, self, keytype))
elem.add("value", Mapping._obj2soap(value, self, valuetype))
# ApacheAxis allows only 'item' here.
soap_obj.add("item", elem)
unless obj.nil?
mark_marshalled_obj(obj, soap_obj)
obj.each do |key, value|
elem = SOAPStruct.new
elem.add("key", Mapping._obj2soap(key, self, keytype))
elem.add("value", Mapping._obj2soap(value, self, valuetype))
# ApacheAxis allows only 'item' here.
soap_obj.add("item", elem)
end
end
soap_obj
end
@ -162,10 +204,64 @@ private
def elements2soap(obj, soap_obj, elements)
elements.each do |element|
name = element.name.name
child_obj = obj.instance_variable_get('@' + name)
soap_obj.add(name, Mapping._obj2soap(child_obj, self, element.type))
child_obj = Mapping.get_attribute(obj, name)
soap_obj.add(name,
Mapping._obj2soap(child_obj, self, element.type || element.name))
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

View file

@ -1,5 +1,5 @@
# 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
# 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/typeMap'
require 'xsd/codegen/gensupport'
require 'xsd/namedelements'
module SOAP
module Mapping
class WSDLLiteralRegistry
class WSDLLiteralRegistry < Registry
attr_reader :definedelements
attr_reader :definedtypes
attr_accessor :excn_handler_obj2soap
attr_accessor :excn_handler_soap2obj
def initialize(definedelements = nil, definedtypes = nil)
@definedelements = definedelements
def initialize(definedtypes = XSD::NamedElements::Empty,
definedelements = XSD::NamedElements::Empty)
@definedtypes = definedtypes
@rubytype_factory = RubytypeFactory.new(:allow_original_mapping => false)
@definedelements = definedelements
@excn_handler_obj2soap = nil
@excn_handler_soap2obj = nil
@schema_element_cache = {}
@schema_attribute_cache = {}
end
def obj2soap(obj, qname)
ret = nil
if !@definedelements.nil? && ele = @definedelements[qname]
ret = _obj2soap(obj, ele)
elsif !@definedtypes.nil? && type = @definedtypes[qname]
ret = obj2type(obj, type)
soap_obj = nil
if ele = @definedelements[qname]
soap_obj = obj2elesoap(obj, ele)
elsif type = @definedtypes[qname]
soap_obj = obj2typesoap(obj, type)
else
ret = unknownobj2soap(obj, qname)
soap_obj = any2soap(obj, qname)
end
return ret if ret
return soap_obj if soap_obj
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)
}
return ret if ret
return soap_obj if soap_obj
end
raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.")
raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
end
# 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
return soapele2obj(node)
return any2obj(node)
rescue MappingError
end
if @excn_handler_soap2obj
@ -62,108 +69,164 @@ class WSDLLiteralRegistry
rescue Exception
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
private
def _obj2soap(obj, ele)
def obj2elesoap(obj, ele)
o = nil
if ele.type
if type = @definedtypes[ele.type]
o = obj2type(obj, type)
o = obj2typesoap(obj, type)
elsif type = TypeMap[ele.type]
o = base2soap(obj, type)
else
raise MappingError.new("Cannot find type #{ele.type}.")
raise MappingError.new("cannot find type #{ele.type}")
end
o.elename = ele.name
elsif ele.local_complextype
o = SOAPElement.new(ele.name)
ele.local_complextype.each_element do |child_ele|
o.add(_obj2soap(Mapping.find_attribute(obj, child_ele.name.name),
child_ele))
end
o = obj2typesoap(obj, ele.local_complextype)
o.elename = ele.name
add_attributes2soap(obj, o)
elsif ele.local_simpletype
o = obj2typesoap(obj, ele.local_simpletype)
o.elename = ele.name
else
raise MappingError.new('Illegal schema?')
raise MappingError.new('illegal schema?')
end
o
end
def obj2type(obj, type)
def obj2typesoap(obj, type)
if type.is_a?(::WSDL::XMLSchema::SimpleType)
simple2soap(obj, type)
simpleobj2soap(obj, type)
else
complex2soap(obj, type)
complexobj2soap(obj, type)
end
end
def simple2soap(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
def simpleobj2soap(obj, type)
type.check_lexical_format(obj)
return SOAPNil.new if obj.nil? # ToDo: check nillable.
o = base2soap(obj, TypeMap[type.base])
o
end
def complex2soap(obj, type)
def complexobj2soap(obj, type)
o = SOAPElement.new(type.name)
type.each_element do |child_ele|
o.add(_obj2soap(Mapping.find_attribute(obj, child_ele.name.name),
child_ele))
child = Mapping.get_attribute(obj, child_ele.name.name)
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
o
end
def unknownobj2soap(obj, name)
if obj.class.class_variables.include?('@@schema_element')
ele = SOAPElement.new(name)
add_elements2soap(obj, ele)
add_attributes2soap(obj, ele)
ele
def any2soap(obj, qname)
if obj.is_a?(SOAPElement)
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 = name
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
else # expected to be a basetype or an anyType.
o = Mapping.obj2soap(obj)
o.elename = name
o
end
end
def stubobj2soap(obj, qname)
ele = SOAPElement.new(qname)
add_elements2soap(obj, ele)
add_attributes2soap(obj, ele)
ele
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
def add_elements2soap(obj, ele)
elements, as_array = schema_element_definition(obj.class)
elements.each do |elename, type|
child = Mapping.find_attribute(obj, elename)
name = ::XSD::QName.new(nil, elename)
if as_array.include?(type)
child.each do |item|
ele.add(obj2soap(item, name))
if elements
elements.each do |elename, type|
child = Mapping.get_attribute(obj, elename)
unless child.nil?
name = XSD::QName.new(nil, elename)
if as_array.include?(type)
child.each do |item|
ele.add(obj2soap(item, name))
end
else
ele.add(obj2soap(child, name))
end
end
else
ele.add(obj2soap(child, name))
end
end
end
def add_attributes2soap(obj, ele)
attributes = schema_attribute_definition(obj.class)
attributes.each do |attrname, param|
attr = Mapping.find_attribute(obj, 'attr_' + attrname)
ele.extraattr[attrname] = attr
if attributes
attributes.each do |qname, param|
attr = obj.__send__('xmlattr_' +
XSD::CodeGen::GenSupport.safevarname(qname.name))
ele.extraattr[qname] = attr
end
end
end
def base2soap(obj, type)
soap_obj = nil
if type <= ::XSD::XSDString
soap_obj = type.new(::XSD::Charset.is_ces(obj, $KCODE) ?
::XSD::Charset.encoding_conv(obj, $KCODE, ::XSD::Charset.encoding) :
obj)
if type <= XSD::XSDString
soap_obj = type.new(XSD::Charset.is_ces(obj, $KCODE) ?
XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) : obj)
else
soap_obj = type.new(obj)
end
@ -176,35 +239,41 @@ private
end
klass = ::SOAP::Mapping::Object
obj = klass.new
node.each do |name, value|
obj.__soap_set_property(name, Mapping.soap2obj(value))
end
obj
end
def soapele2obj(node, obj_class = nil)
def any2obj(node, obj_class = nil)
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)
end
if obj_class and obj_class.class_variables.include?('@@schema_element')
soapele2definedobj(node, obj_class)
elsif node.is_a?(SOAPElement)
node.to_obj
soapele2stubobj(node, obj_class)
elsif node.is_a?(SOAPElement) or node.is_a?(SOAPStruct)
# SOAPArray for literal?
soapele2plainobj(node)
else
result, obj = @rubytype_factory.soap2obj(nil, node, nil, self)
obj = Mapping._soap2obj(node, Mapping::DefaultRegistry, obj_class)
add_attributes2plainobj(node, obj)
obj
end
end
def soapele2definedobj(node, obj_class)
def soapele2stubobj(node, obj_class)
obj = Mapping.create_empty_object(obj_class)
add_elements2obj(node, obj)
add_attributes2obj(node, obj)
add_elements2stubobj(node, obj)
add_attributes2stubobj(node, obj)
obj
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)
vars = {}
node.each do |name, value|
@ -217,13 +286,13 @@ private
child = klass.new(nil).data
end
else
child = soapele2obj(value, klass)
child = any2obj(value, klass)
end
else
raise MappingError.new("Unknown class: #{class_name}")
raise MappingError.new("unknown class: #{class_name}")
end
else # untyped element is treated as anyType.
child = anytype2obj(value)
child = any2obj(value)
end
if as_array.include?(class_name)
(vars[name] ||= []) << child
@ -231,48 +300,92 @@ private
vars[name] = child
end
end
Mapping.set_instance_vars(obj, vars)
Mapping.set_attributes(obj, vars)
end
def add_attributes2obj(node, obj)
Mapping.set_instance_vars(obj, {'__soap_attribute' => {}})
vars = {}
attributes = schema_attribute_definition(obj.class)
attributes.each do |attrname, class_name|
attr = node.extraattr[::XSD::QName.new(nil, attrname)]
next if attr.nil? or attr.empty?
klass = Mapping.class_from_name(class_name)
if klass.ancestors.include?(::SOAP::SOAPBasetype)
child = klass.new(attr).data
else
child = attr
def add_attributes2stubobj(node, obj)
if attributes = schema_attribute_definition(obj.class)
define_xmlattr(obj)
attributes.each do |qname, class_name|
attr = node.extraattr[qname]
next if attr.nil? or attr.empty?
klass = Mapping.class_from_name(class_name)
if klass.ancestors.include?(::SOAP::SOAPBasetype)
child = klass.new(attr).data
else
child = attr
end
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
vars['attr_' + attrname] = child
end
Mapping.set_instance_vars(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)
if @schema_element_cache.key?(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]
@schema_element_cache[klass] ||= Mapping.schema_element_definition(klass)
end
def schema_attribute_definition(klass)
attributes = klass.class_eval('@@schema_attribute')
@schema_attribute_cache[klass] ||= Mapping.schema_attribute_definition(klass)
end
end

View file

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

View file

@ -43,17 +43,22 @@ class NetHttpClient
raise NotImplementedError.new("not supported for now")
end
def proxy=(proxy_str)
if proxy_str.nil?
def proxy=(proxy)
if proxy.nil?
@proxy = nil
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
@proxy.host == nil or @proxy.port == nil
raise ArgumentError.new("unsupported proxy `#{proxy_str}'")
raise ArgumentError.new("unsupported proxy `#{proxy}'")
end
@proxy
end
reset_all
@proxy
end
def set_basic_auth(uri, user_id, passwd)
@ -79,7 +84,9 @@ class NetHttpClient
end
def post(url, req_body, header = {})
url = URI.parse(url)
unless url.is_a?(URI)
url = URI.parse(url)
end
extra = header.dup
extra['User-Agent'] = @agent if @agent
res = start(url) { |http|
@ -89,7 +96,9 @@ class NetHttpClient
end
def get_content(url, header = {})
url = URI.parse(url)
unless url.is_a?(URI)
url = URI.parse(url)
end
extra = header.dup
extra['User-Agent'] = @agent if @agent
res = start(url) { |http|

View file

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

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -22,32 +22,55 @@ module RPC
class Driver
class EmptyResponseError < Error; end
class << self
def __attr_proxy(symbol, assignable = false)
name = symbol.to_s
self.__send__(:define_method, name, proc {
@servant.__send__(name)
})
if assignable
self.__send__(:define_method, name + '=', proc { |rhs|
@servant.__send__(name + '=', rhs)
if RUBY_VERSION >= "1.7.0"
def __attr_proxy(symbol, assignable = false)
name = symbol.to_s
self.__send__(:define_method, name, proc {
@proxy.__send__(name)
})
if assignable
self.__send__(:define_method, name + '=', proc { |rhs|
@proxy.__send__(name + '=', rhs)
})
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
__attr_proxy :options
__attr_proxy :headerhandler
__attr_proxy :streamhandler
__attr_proxy :test_loopback_response
__attr_proxy :endpoint_url, true
__attr_proxy :mapping_registry, true
__attr_proxy :soapaction, true
__attr_proxy :default_encodingstyle, true
__attr_proxy :generate_explicit_type, 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
options["protocol.http.proxy"]
@ -81,10 +104,12 @@ class Driver
options["protocol.wiredump_file_base"] = wiredump_file_base
end
def initialize(endpoint_url, namespace, soapaction = nil)
@servant = Servant__.new(self, endpoint_url, namespace)
@servant.soapaction = soapaction
@proxy = @servant.proxy
def initialize(endpoint_url, namespace = nil, soapaction = nil)
@namespace = namespace
@soapaction = soapaction
@options = setup_options
@wiredump_file_base = nil
@proxy = Proxy.new(endpoint_url, @soapaction, @options)
end
def loadproperty(propertyname)
@ -93,28 +118,23 @@ class Driver
end
end
def inspect
"#<#{self.class}:#{@servant.inspect}>"
end
def add_rpc_method(name, *params)
param_def = create_rpc_param_def(params)
@servant.add_rpc_method(name, @servant.soapaction, name, param_def)
add_rpc_method_with_soapaction_as(name, name, @soapaction, *params)
end
def add_rpc_method_as(name, name_as, *params)
param_def = create_rpc_param_def(params)
@servant.add_rpc_method(name_as, @servant.soapaction, name, param_def)
add_rpc_method_with_soapaction_as(name, name_as, @soapaction, *params)
end
def add_rpc_method_with_soapaction(name, soapaction, *params)
param_def = create_rpc_param_def(params)
@servant.add_rpc_method(name, soapaction, name, param_def)
add_rpc_method_with_soapaction_as(name, name, soapaction, *params)
end
def add_rpc_method_with_soapaction_as(name, name_as, soapaction, *params)
param_def = create_rpc_param_def(params)
@servant.add_rpc_method(name_as, soapaction, name, param_def)
param_def = SOAPMethod.create_rpc_param_def(params)
qname = XSD::QName.new(@namespace, name_as)
@proxy.add_rpc_method(qname, soapaction, name, param_def)
add_rpc_method_interface(name, param_def)
end
# add_method is for shortcut of typical rpc/encoded method definition.
@ -123,226 +143,107 @@ class Driver
alias add_method_with_soapaction add_rpc_method_with_soapaction
alias add_method_with_soapaction_as add_rpc_method_with_soapaction_as
def add_document_method(name, req_qname, res_qname)
param_def = create_document_param_def(name, req_qname, res_qname)
@servant.add_document_method(name, @servant.soapaction, name, param_def)
def add_document_method(name, soapaction, req_qname, res_qname)
param_def = SOAPMethod.create_doc_param_def(req_qname, res_qname)
@proxy.add_document_method(soapaction, name, param_def)
add_document_method_interface(name, param_def)
end
def add_document_method_as(name, name_as, req_qname, res_qname)
param_def = create_document_param_def(name, req_qname, res_qname)
@servant.add_document_method(name_as, @servant.soapaction, name, param_def)
def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
@proxy.add_rpc_operation(qname, soapaction, name, param_def, opt)
add_rpc_method_interface(name, param_def)
end
def add_document_method_with_soapaction(name, soapaction, req_qname,
res_qname)
param_def = create_document_param_def(name, req_qname, res_qname)
@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
def add_document_operation(soapaction, name, param_def, opt = {})
@proxy.add_document_operation(soapaction, name, param_def, opt)
add_document_method_interface(name, param_def)
end
def invoke(headers, body)
@servant.invoke(headers, body)
if headers and !headers.is_a?(SOAPHeader)
headers = create_header(headers)
end
set_wiredump_file_base(body.elename.name)
env = @proxy.invoke(headers, body)
if env.nil?
return nil, nil
else
return env.header, env.body
end
end
def call(name, *params)
@servant.call(name, *params)
set_wiredump_file_base(name)
@proxy.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)
def set_wiredump_file_base(name)
if @wiredump_file_base
@proxy.set_wiredump_file_base("#{@wiredump_file_base}_#{name}")
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]]
]
def create_header(headers)
header = SOAPHeader.new()
headers.each do |content, mustunderstand, encodingstyle|
header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
end
header
end
def setup_options
if opt = Property.loadproperty(::SOAP::PropertyName)
opt = opt["client"]
end
opt ||= Property.new
opt.add_hook("protocol.mandatorycharset") do |key, value|
@proxy.mandatorycharset = value
end
opt.add_hook("protocol.wiredump_file_base") do |key, value|
@wiredump_file_base = value
end
opt["protocol.http.charset"] ||= XSD::Charset.encoding_label
opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
opt
end
def add_rpc_method_interface(name, param_def)
@servant.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, paramname)
@servant.add_document_method_interface(name, paramname)
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
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
def invoke(headers, body)
if headers and !headers.is_a?(SOAPHeader)
headers = create_header(headers)
end
set_wiredump_file_base(body.elename.name)
env = @proxy.invoke(headers, body)
if env.nil?
return nil, nil
else
return env.header, env.body
end
end
def call(name, *params)
set_wiredump_file_base(name)
@proxy.call(name, *params)
end
def 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
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|
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})")
"wrong number of arguments (#{arg.size} for #{param_count})")
end
@servant.call(name, *arg)
})
@host.method(name)
call(name, *arg)
end
self.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)
if @wiredump_file_base
@proxy.set_wiredump_file_base(@wiredump_file_base + "_#{ name }")
end
end
def create_header(headers)
header = SOAPHeader.new()
headers.each do |content, mustunderstand, encodingstyle|
header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
end
header
end
def setup_options
if opt = Property.loadproperty(::SOAP::PropertyName)
opt = opt["client"]
end
opt ||= Property.new
opt.add_hook("protocol.mandatorycharset") do |key, value|
@proxy.mandatorycharset = value
end
opt.add_hook("protocol.wiredump_file_base") do |key, value|
@wiredump_file_base = value
end
opt["protocol.http.charset"] ||= XSD::Charset.encoding_label
opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
opt
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

View file

@ -1,5 +1,5 @@
# 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
# 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 :inparam
attr_reader :outparam
attr_reader :retval_name
attr_reader :retval_class_name
def initialize(qname, param_def = nil)
super(nil)
@ -93,6 +95,7 @@ class SOAPMethod < SOAPStruct
@inparam = {}
@outparam = {}
@retval_name = nil
@retval_class_name = nil
init_param(@param_def) if @param_def
end
@ -101,12 +104,12 @@ class SOAPMethod < SOAPStruct
@outparam_names.size > 0
end
def each_param_name(*type)
@signature.each do |io_type, name, param_type|
if type.include?(io_type)
yield(name)
end
end
def input_params
collect_params(IN, INOUT)
end
def output_params
collect_params(OUT, INOUT)
end
def set_param(params)
@ -124,7 +127,30 @@ class SOAPMethod < SOAPStruct
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_names.each do |param_name|
param_def.push([IN, param_name, nil])
@ -133,8 +159,29 @@ class SOAPMethod < SOAPStruct
param_def
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
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)
param_def.each do |io_type, name, param_type|
case io_type
@ -148,12 +195,20 @@ private
@signature.push([INOUT, name, param_type])
@inoutparam_names.push(name)
when RETVAL
if (@retval_name)
raise MethodDefinitionError.new('Duplicated retval')
if @retval_name
raise MethodDefinitionError.new('duplicated retval')
end
@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
@retval_class_name = param_type[0]
end
end
else
raise MethodDefinitionError.new("Unknown type: #{ io_type }")
raise MethodDefinitionError.new("unknown type: #{io_type}")
end
end
end
@ -168,7 +223,7 @@ class SOAPMethodRequest < SOAPMethod
param_value = []
i = 0
params.each do |param|
param_name = "p#{ i }"
param_name = "p#{i}"
i += 1
param_def << [IN, param_name, nil]
param_value << [param_name, param]
@ -186,9 +241,9 @@ class SOAPMethodRequest < SOAPMethod
end
def each
each_param_name(IN, INOUT) do |name|
input_params.each do |name|
unless @inparam[name]
raise ParameterError.new("Parameter: #{ name } was not given.")
raise ParameterError.new("parameter: #{name} was not given")
end
yield(name, @inparam[name])
end
@ -200,10 +255,10 @@ class SOAPMethodRequest < SOAPMethod
req
end
def create_method_response
SOAPMethodResponse.new(
XSD::QName.new(@elename.namespace, @elename.name + 'Response'),
@param_def)
def create_method_response(response_name = nil)
response_name ||=
XSD::QName.new(@elename.namespace, @elename.name + 'Response')
SOAPMethodResponse.new(response_name, @param_def)
end
private
@ -211,7 +266,7 @@ private
def check_elename(qname)
# NCName & ruby's method 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
@ -236,11 +291,11 @@ class SOAPMethodResponse < SOAPMethod
yield(@retval_name, @retval)
end
each_param_name(OUT, INOUT) do |param_name|
unless @outparam[param_name]
raise ParameterError.new("Parameter: #{ param_name } was not given.")
output_params.each do |name|
unless @outparam[name]
raise ParameterError.new("parameter: #{name} was not given")
end
yield(param_name, @outparam[param_name])
yield(name, @outparam[name])
end
end
end

View file

@ -24,55 +24,62 @@ class HTTPServer < Logger::Application
super(config[:SOAPHTTPServerApplicationName] || self.class.name)
@default_namespace = config[:SOAPDefaultNamespace]
@webrick_config = config.dup
self.level = Logger::Severity::ERROR # keep silent by default
@webrick_config[:Logger] ||= @log
@server = nil
@soaplet = ::SOAP::RPC::SOAPlet.new
self.level = Logger::Severity::INFO
@log = @webrick_config[:Logger] # sync logger of App and HTTPServer
@router = ::SOAP::RPC::Router.new(self.class.name)
@soaplet = ::SOAP::RPC::SOAPlet.new(@router)
on_init
@server = WEBrick::HTTPServer.new(@webrick_config)
@server.mount('/', @soaplet)
end
def on_init
# define extra methods in derived class.
# do extra initialization in a derived class if needed.
end
def status
if @server
@server.status
else
nil
end
@server.status if @server
end
def shutdown
@server.shutdown if @server
end
def mapping_registry
@soaplet.app_scope_router.mapping_registry
@router.mapping_registry
end
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
# servant entry interface
def add_rpc_request_servant(factory, namespace = @default_namespace,
mapping_registry = nil)
@soaplet.add_rpc_request_servant(factory, namespace, mapping_registry)
def add_rpc_request_servant(factory, namespace = @default_namespace)
@router.add_rpc_request_servant(factory, namespace)
end
def add_rpc_servant(obj, namespace = @default_namespace)
@soaplet.add_rpc_servant(obj, namespace)
@router.add_rpc_servant(obj, namespace)
end
def add_rpc_request_headerhandler(factory)
@soaplet.add_rpc_request_headerhandler(factory)
def add_request_headerhandler(factory)
@router.add_request_headerhandler(factory)
end
def add_rpc_headerhandler(obj)
@soaplet.add_rpc_headerhandler(obj)
def add_headerhandler(obj)
@router.add_headerhandler(obj)
end
alias add_rpc_headerhandler add_headerhandler
# method entry interface
@ -81,52 +88,38 @@ class HTTPServer < Logger::Application
end
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)
qname = XSD::QName.new(@default_namespace, name_as)
soapaction = nil
param_def = create_param_def(obj, name, param)
add_operation(qname, soapaction, obj, name, param_def)
param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
@router.add_rpc_operation(obj, qname, soapaction, name, param_def)
end
alias add_method_as add_rpc_method_as
def add_operation(qname, soapaction, obj, name, param_def, opt = {})
opt[:request_style] ||= :rpc
opt[:response_style] ||= :rpc
opt[:request_use] ||= :encoded
opt[:response_use] ||= :encoded
@soaplet.app_scope_router.add_operation(qname, soapaction, obj, name,
param_def, opt)
def add_document_method(obj, soapaction, name, req_qnames, res_qnames)
param_def = SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
@router.add_document_operation(obj, soapaction, name, param_def)
end
def create_param_def(obj, name, param = nil)
if param.nil? or param.empty?
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
def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
@router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
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
private
def run
@server = WEBrick::HTTPServer.new(@webrick_config)
@server.mount('/', @soaplet)
@server.start
end
end

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -78,30 +78,62 @@ public
@streamhandler.test_loopback_response
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[:response_style] ||= :rpc
opt[:request_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
def add_document_method(qname, soapaction, name, param_def, opt = {})
def add_document_operation(soapaction, name, param_def, opt = {})
opt[:request_style] ||= :document
opt[:response_style] ||= :document
opt[:request_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
# 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)
opt[:external_content] = nil
conn_data = marshal(req_env, opt)
if ext = opt[:external_content]
reqopt[:external_content] = nil
conn_data = marshal(req_env, reqopt)
if ext = reqopt[:external_content]
mime = MIMEMessage.new
ext.each do |k, v|
mime.add_attachment(v.data)
@ -111,33 +143,12 @@ public
conn_data.send_string = mime.content_str
conn_data.send_contenttype = mime.headers['content-type'].str
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?
return nil
end
unmarshal(conn_data, 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 = 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)
unmarshal(conn_data, resopt)
end
def check_fault(body)
@ -217,98 +228,203 @@ private
attr_reader :request_use
attr_reader :response_use
def initialize(qname, soapaction, name, param_def, opt)
def initialize(soapaction, param_def, opt)
@soapaction = soapaction
@request_style = opt[:request_style]
@response_style = opt[:response_style]
@request_use = opt[:request_use]
@response_use = opt[:response_use]
@rpc_method_factory = @document_method_name = nil
check_style(@request_style)
check_style(@response_style)
check_use(@request_use)
check_use(@response_use)
if @request_style == :rpc
@rpc_method_factory = SOAPMethodRequest.new(qname, param_def,
@soapaction)
@rpc_request_qname = opt[:request_qname]
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
@document_method_name = {}
@doc_request_qnames = []
@doc_response_qnames = []
param_def.each do |inout, paramname, typeinfo|
klass, namespace, name = typeinfo
case inout.to_s
when "input"
@document_method_name[:input] = ::XSD::QName.new(namespace, name)
when "output"
@document_method_name[:output] = ::XSD::QName.new(namespace, name)
klass_not_used, nsdef, namedef = typeinfo
if namedef.nil?
raise MethodDefinitionError.new("qname must be given")
end
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 MethodDefinitionError, "unknown type: " + inout
raise MethodDefinitionError.new(
"illegal inout definition for document style: #{inout}")
end
end
end
end
def request_default_encodingstyle
(@request_style == :rpc) ? EncodingNamespace : LiteralNamespace
(@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
def response_default_encodingstyle
(@response_style == :rpc) ? EncodingNamespace : LiteralNamespace
(@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
# for rpc
def each_param_name(*target)
def request_body(values, mapping_registry, literal_mapping_registry)
if @request_style == :rpc
@rpc_method_factory.each_param_name(*target) do |name|
yield(name)
end
request_rpc(values, mapping_registry, literal_mapping_registry)
else
yield(@document_method_name[:input].name)
request_doc(values, mapping_registry, literal_mapping_registry)
end
end
def create_request_body(values, 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)
def response_obj(body, mapping_registry, literal_mapping_registry)
if @response_style == :rpc
ret = env.body.response ?
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
ret
end
response_rpc(body, mapping_registry, literal_mapping_registry)
else
Mapping.soap2obj(env.body.root_node, literal_mapping_registry)
response_doc(body, mapping_registry, literal_mapping_registry)
end
end
def raise_fault(e, mapping_registry, literal_mapping_registry)
if @response_style == :rpc
Mapping.fault2exception(e, mapping_registry)
else
Mapping.fault2exception(e, literal_mapping_registry)
end
end
private
ALLOWED_STYLE = [:rpc, :document]
def check_style(style)
unless ALLOWED_STYLE.include?(style)
raise MethodDefinitionError, "unknown style: " + style
unless [:rpc, :document].include?(style)
raise MethodDefinitionError.new("unknown style: #{style}")
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

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -25,101 +25,229 @@ class Router
include SOAP
attr_reader :actor
attr_accessor :allow_unqualified_element
attr_accessor :default_encodingstyle
attr_accessor :mapping_registry
attr_accessor :literal_mapping_registry
attr_reader :headerhandler
attr_accessor :generate_explicit_type
def initialize(actor)
@actor = actor
@allow_unqualified_element = false
@default_encodingstyle = nil
@mapping_registry = nil
@headerhandler = Header::HandlerSet.new
@literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new
@operation = {}
@generate_explicit_type = true
@operation_by_soapaction = {}
@operation_by_qname = {}
@headerhandlerfactory = []
end
def add_rpc_method(receiver, qname, soapaction, name, param_def, opt = {})
opt[:request_style] ||= :rpc
opt[:response_style] ||= :rpc
opt[:request_use] ||= :encoded
opt[:response_use] ||= :encoded
add_operation(qname, soapaction, receiver, name, param_def, opt)
end
def add_document_method(receiver, qname, soapaction, name, param_def, opt = {})
opt[:request_style] ||= :document
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)
###
## header handler interface
#
def add_request_headerhandler(factory)
unless factory.respond_to?(:create)
raise TypeError.new("factory must respond to 'create'")
end
add_operation(qname, soapaction, receiver, name, param_def, opt)
@headerhandlerfactory << factory
end
def add_operation(qname, soapaction, receiver, name, param_def, opt)
@operation[fqname(qname)] = Operation.new(qname, soapaction, receiver,
name, param_def, opt)
def add_headerhandler(handler)
@headerhandler.add(handler)
end
# add_method is for shortcut of typical use="encoded" method definition.
alias add_method add_rpc_method
###
## servant definition interface
#
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
def add_rpc_servant(obj, namespace)
::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)
soap_response = nil
# we cannot set request_default_encodingsyle before parsing the content.
env = unmarshal(conn_data)
if env.nil?
raise ArgumentError.new("illegal SOAP marshal format")
end
op = lookup_operation(conn_data.soapaction, env.body)
headerhandler = @headerhandler.dup
@headerhandlerfactory.each do |f|
headerhandler.add(f.create)
end
receive_headers(headerhandler, env.header)
soap_response = default_encodingstyle = nil
begin
env = unmarshal(conn_data)
if env.nil?
raise ArgumentError.new("Illegal SOAP marshal format.")
end
receive_headers(env.header)
request = env.body.request
op = @operation[fqname(request.elename)]
unless op
raise RPCRoutingError.new("Method: #{request.elename} not supported.")
end
soap_response = op.call(request, @mapping_registry, @literal_mapping_registry)
soap_response =
op.call(env.body, @mapping_registry, @literal_mapping_registry)
default_encodingstyle = op.response_default_encodingstyle
rescue Exception
soap_response = fault($!)
conn_data.is_fault = true
default_encodingstyle = nil
end
marshal(conn_data, op, soap_response)
conn_data
conn_data.is_fault = true if soap_response.is_a?(SOAPFault)
header = call_headers(headerhandler)
body = SOAPBody.new(soap_response)
env = SOAPEnvelope.new(header, body)
marshal(conn_data, env, default_encodingstyle)
end
# Create fault response string.
def create_fault_response(e, charset = nil)
header = SOAPHeader.new
body = SOAPBody.new(fault(e))
env = SOAPEnvelope.new(header, body)
opt = options
def create_fault_response(e)
env = SOAPEnvelope.new(SOAPHeader.new, SOAPBody.new(fault(e)))
opt = {}
opt[:external_content] = nil
opt[:charset] = charset
response_string = Processor.marshal(env, opt)
conn_data = StreamHandler::ConnectionData.new(response_string)
conn_data.is_fault = true
if ext = opt[:external_content]
mime = MIMEMessage.new
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
mimeize(conn_data, ext)
end
conn_data
end
private
def call_headers
headers = @headerhandler.on_outbound
def first_input_part_qname(param_def)
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?
nil
else
@ -131,17 +259,17 @@ private
end
end
def receive_headers(headers)
@headerhandler.on_inbound(headers) if headers
def receive_headers(headerhandler, headers)
headerhandler.on_inbound(headers) if headers
end
def unmarshal(conn_data)
opt = options
opt = {}
contenttype = conn_data.receive_contenttype
if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
opt[:external_content] = {}
mime = MIMEMessage.parse("Content-Type: " + contenttype,
conn_data.receive_string)
conn_data.receive_string)
mime.parts.each do |part|
value = Attachment.new(part.content)
value.contentid = part.contentid
@ -160,28 +288,29 @@ private
env
end
def marshal(conn_data, op, soap_response)
response_opt = options
response_opt[:external_content] = nil
if op and !conn_data.is_fault and op.response_use == :document
response_opt[:default_encodingstyle] =
::SOAP::EncodingStyle::ASPDotNetHandler::Namespace
end
header = call_headers
body = SOAPBody.new(soap_response)
env = SOAPEnvelope.new(header, body)
response_string = Processor.marshal(env, response_opt)
def marshal(conn_data, env, default_encodingstyle = nil)
opt = {}
opt[:external_content] = nil
opt[:default_encodingstyle] = default_encodingstyle
opt[:generate_explicit_type] = @generate_explicit_type
response_string = Processor.marshal(env, opt)
conn_data.send_string = response_string
if ext = response_opt[:external_content]
mime = MIMEMessage.new
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
if ext = opt[:external_content]
mimeize(conn_data, ext)
end
conn_data
end
def mimeize(conn_data, ext)
mime = MIMEMessage.new
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
conn_data
end
# Create fault response.
@ -194,84 +323,156 @@ private
Mapping.obj2soap(detail, @mapping_registry))
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
attr_reader :receiver
attr_reader :name
attr_reader :soapaction
attr_reader :request_style
attr_reader :response_style
attr_reader :request_use
attr_reader :response_use
def initialize(qname, soapaction, receiver, name, param_def, opt)
def initialize(soapaction, name, param_def, opt)
@soapaction = soapaction
@receiver = receiver
@name = name
@request_style = opt[:request_style]
@response_style = opt[:response_style]
@request_use = opt[:request_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
@rpc_response_factory =
RPC::SOAPMethodRequest.new(qname, param_def, @soapaction)
request_qname = opt[:request_qname] or raise
@rpc_method_factory =
RPC::SOAPMethodRequest.new(request_qname, param_def, @soapaction)
@rpc_response_qname = opt[:response_qname]
else
outputdef = param_def.find { |inout, paramname, typeinfo| inout == "output" }
klass, nsdef, namedef = outputdef[2]
@document_response_qname = ::XSD::QName.new(nsdef, namedef)
@doc_request_qnames = []
@doc_response_qnames = []
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
def call(request, mapping_registry, literal_mapping_registry)
def request_default_encodingstyle
(@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
def response_default_encodingstyle
(@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
def call(body, mapping_registry, literal_mapping_registry)
if @request_style == :rpc
param = Mapping.soap2obj(request, mapping_registry)
result = rpc_call(request, param)
values = request_rpc(body, mapping_registry, literal_mapping_registry)
else
param = Mapping.soap2obj(request, literal_mapping_registry)
result = document_call(request, param)
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
rpc_response(result, mapping_registry)
response_rpc(result, mapping_registry, literal_mapping_registry)
else
document_response(result, literal_mapping_registry)
response_doc(result, mapping_registry, literal_mapping_registry)
end
end
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)
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
values = request.collect { |key, value| param[key] }
@receiver.method(@name.intern).call(*values)
end
def document_call(request, param)
@receiver.method(@name.intern).call(param)
def request_document(body, mapping_registry, literal_mapping_registry)
# 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
def rpc_response(result, mapping_registry)
soap_response = @rpc_response_factory.create_method_response
def request_rpc_enc(request, mapping_registry)
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?
unless result.is_a?(Array)
raise RPCRoutingError.new("Out parameter was not returned.")
raise RPCRoutingError.new("out parameter was not returned")
end
outparams = {}
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)
i += 1
end
@ -283,8 +484,83 @@ private
soap_response
end
def document_response(result, literal_mapping_registry)
literal_mapping_registry.obj2soap(result, @document_response_qname)
def response_rpc_lit(result, mapping_registry)
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

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -14,7 +14,23 @@ begin
require 'stringio'
require 'zlib'
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
@ -24,61 +40,28 @@ module RPC
class SOAPlet < WEBrick::HTTPServlet::AbstractServlet
public
attr_reader :app_scope_router
attr_reader :options
def initialize
@rpc_router_map = {}
@app_scope_router = ::SOAP::RPC::Router.new(self.class.name)
@headerhandlerfactory = []
@app_scope_headerhandler = nil
def initialize(router = nil)
@router = router || ::SOAP::RPC::Router.new(self.class.name)
@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
def allow_content_encoding_gzip=(allow)
@options[:allow_content_encoding_gzip] = allow
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.
#
@ -93,111 +76,63 @@ public
def do_GET(req, res)
res.header['Allow'] = 'POST'
raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed."
raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed"
end
def do_POST(req, res)
@config[:Logger].debug { "SOAP request: " + req.body }
soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
router = lookup_router(soapaction)
with_headerhandler(router) do |router|
begin
conn_data = ::SOAP::StreamHandler::ConnectionData.new
conn_data.receive_string = req.body
conn_data.receive_contenttype = req['content-type']
conn_data = router.route(conn_data)
res['content-type'] = conn_data.send_contenttype
if conn_data.is_fault
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
end
if outstring = encode_gzip(req, conn_data.send_string)
res['content-encoding'] = 'gzip'
res['content-length'] = outstring.size
res.body = outstring
else
res.body = conn_data.send_string
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
logger.debug { "SOAP request: " + req.body } if logger
begin
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
@config[:Logger].debug { "SOAP response: (chunked response not logged)" }
logger.debug { "SOAP response: (chunked response not logged)" } if logger
else
@config[:Logger].debug { "SOAP response: " + res.body }
logger.debug { "SOAP response: " + res.body } if logger
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
def logger
@config[:Logger]
end
def setup_rpc_request_router(namespace)
router = @rpc_router_map[namespace] || RequestRouter.new(:rpc, namespace)
add_rpc_router(namespace, router)
router
def setup_req(conn_data, req)
conn_data.receive_string = req.body
conn_data.receive_contenttype = req['content-type']
conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
end
def add_rpc_router(namespace, router)
@rpc_router_map[namespace] = router
def setup_res(conn_data, req, res)
res['content-type'] = conn_data.send_contenttype
if conn_data.is_fault
res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
end
if outstring = encode_gzip(req, conn_data.send_string)
res['content-encoding'] = 'gzip'
res['content-length'] = outstring.size
res.body = outstring
else
res.body = conn_data.send_string
end
end
def parse_soapaction(soapaction)
if /^"(.*)"$/ =~ soapaction
soapaction = $1
end
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
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) }
if !soapaction.nil? and !soapaction.empty?
if /^"(.+)"$/ =~ soapaction
return $1
end
end
nil
end
def encode_gzip(req, outstring)
@ -219,32 +154,6 @@ private
req['accept-encoding'] and
req['accept-encoding'].split(/,\s*/).include?('gzip')
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

View file

@ -13,7 +13,7 @@ require 'xsd/charset'
module SOAP
Version = '1.5.3-ruby1.8.2'
VERSION = Version = '1.5.4'
PropertyName = 'soap/property'
EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/'
@ -75,6 +75,7 @@ class ArrayIndexOutOfBoundsError < Error; end
class ArrayStoreError < Error; end
class RPCRoutingError < Error; end
class EmptyResponseError < Error; end
class UnhandledMustUnderstandHeaderError < Error; end
@ -101,6 +102,7 @@ class FaultError < Error
end
end
module Env
def self.getenv(name)
ENV[name.downcase] || ENV[name.upcase]
@ -113,3 +115,25 @@ 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/property'
require 'soap/httpconfigloader'
begin
require 'stringio'
require 'zlib'
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
@ -27,7 +27,7 @@ class StreamHandler
end
HTTPAccess2::Client
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'
SOAP::NetHttpClient
end
@ -40,6 +40,7 @@ class StreamHandler
attr_accessor :receive_string
attr_accessor :receive_contenttype
attr_accessor :is_fault
attr_accessor :soapaction
def initialize(send_string = nil)
@send_string = send_string
@ -47,6 +48,7 @@ class StreamHandler
@receive_string = nil
@receive_contenttype = nil
@is_fault = false
@soapaction = nil
end
end
@ -79,7 +81,7 @@ public
super()
@client = Client.new(nil, "SOAP4R/#{ Version }")
@wiredump_file_base = nil
@charset = @wiredump_dev = @nil
@charset = @wiredump_dev = nil
@options = options
set_options
@client.debug_dev = @wiredump_dev
@ -100,7 +102,8 @@ public
end
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
def reset(endpoint_url = nil)
@ -115,24 +118,7 @@ public
private
def set_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
set_cookie_store_file(@options["cookie_store_file"])
@options.add_hook("cookie_store_file") do |key, value|
set_cookie_store_file(value)
end
HTTPConfigLoader.set_options(@client, @options)
@charset = @options["charset"] || XSD::Charset.charset_label($KCODE)
@options.add_hook("charset") do |key, value|
@charset = value
@ -142,96 +128,23 @@ private
@wiredump_dev = value
@client.debug_dev = @wiredump_dev
end
ssl_config = @options["ssl_config"] ||= ::SOAP::Property.new
set_ssl_config(ssl_config)
ssl_config.add_hook(true) do |key, 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
set_cookie_store_file(@options["cookie_store_file"])
@options.add_hook("cookie_store_file") do |key, value|
set_cookie_store_file(value)
end
ssl_config = @options["ssl_config"]
basic_auth = @options["basic_auth"]
@options.lock(true)
ssl_config.unlock
basic_auth.unlock
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)
@cookie_store = value
@client.set_cookie_store(@cookie_store) if @cookie_store
end
def set_ssl_config(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.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)
def send_post(endpoint_url, conn_data, charset)
conn_data.send_contenttype ||= StreamHandler.create_media_type(charset)
if @wiredump_file_base
@ -243,7 +156,7 @@ private
extra = {}
extra['Content-Type'] = conn_data.send_contenttype
extra['SOAPAction'] = "\"#{ soapaction }\""
extra['SOAPAction'] = "\"#{ conn_data.soapaction }\""
extra['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip?
send_string = conn_data.send_string
@wiredump_dev << "Wire dump:\n\n" if @wiredump_dev

View file

@ -1,5 +1,5 @@
# 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
# redistribute it and/or modify it under the same terms of Ruby's license;
@ -9,19 +9,11 @@
require 'wsdl/parser'
require 'wsdl/importer'
require 'xsd/qname'
require 'soap/element'
require 'soap/baseData'
require 'soap/streamHandler'
require 'soap/mimemessage'
require 'soap/mapping'
require 'xsd/codegen/gensupport'
require 'soap/mapping/wsdlencodedregistry'
require 'soap/mapping/wsdlliteralregistry'
require 'soap/rpc/rpc'
require 'soap/rpc/element'
require 'soap/rpc/proxy'
require 'soap/processor'
require 'soap/header/handlerset'
require 'xsd/codegen/gensupport'
require 'soap/rpc/driver'
require 'wsdl/soap/methodDefCreator'
module SOAP
@ -32,35 +24,27 @@ class WSDLDriverFactory
attr_reader :wsdl
def initialize(wsdl, logdev = nil)
@logdev = logdev
def initialize(wsdl)
@wsdl = import(wsdl)
@methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@wsdl)
end
def inspect
"#<#{self.class}:#{@wsdl.name}>"
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)
service = if servicename
@wsdl.service(XSD::QName.new(@wsdl.targetnamespace, servicename))
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
warn("WSDLDriverFactory#create_driver is depricated. Use create_rpc_driver instead.")
port = find_port(servicename, portname)
WSDLDriver.new(@wsdl, port, @logdev)
end
@ -68,24 +52,137 @@ class WSDLDriverFactory
alias createDriver create_driver
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)
WSDL::Importer.import(location)
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
class WSDLDriver
class << self
def __attr_proxy(symbol, assignable = false)
name = symbol.to_s
self.__send__(:define_method, name, proc {
@servant.__send__(name)
})
if assignable
self.__send__(:define_method, name + '=', proc { |rhs|
@servant.__send__(name + '=', rhs)
end
if RUBY_VERSION >= "1.7.0"
def __attr_proxy(symbol, assignable = false)
name = symbol.to_s
self.__send__(:define_method, name, proc {
@servant.__send__(name)
})
if assignable
self.__send__(:define_method, name + '=', proc { |rhs|
@servant.__send__(name + '=', rhs)
})
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
@ -179,8 +276,10 @@ class WSDLDriver
@wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
@rpc_decode_typemap = @wsdl_types +
@wsdl.soap_rpc_complextypes(port.find_binding)
@wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(@rpc_decode_typemap)
@doc_mapper = Mapping::WSDLLiteralRegistry.new(@wsdl_elements, @wsdl_types)
@wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(
@rpc_decode_typemap)
@doc_mapper = Mapping::WSDLLiteralRegistry.new(
@wsdl_types, @wsdl_elements)
endpoint_url = @port.soap_address.location
# Convert a map which key is QName, to a Hash which key is String.
@operation = {}
@ -222,15 +321,16 @@ class WSDLDriver
def rpc_call(name, *values)
set_wiredump_file_base(name)
unless op_info = @operation[name]
raise MethodDefinitionError, "Method: #{name} not defined."
raise RuntimeError, "method: #{name} not defined"
end
req_header = create_request_header
req_body = create_request_body(op_info, *values)
opt = create_options({
:soapaction => op_info.soapaction || @soapaction,
reqopt = create_options({
:soapaction => op_info.soapaction || @soapaction})
resopt = create_options({
:decode_typemap => @rpc_decode_typemap})
env = @proxy.invoke(req_header, req_body, opt)
raise EmptyResponseError.new("Empty response.") unless env
env = @proxy.route(req_header, req_body, reqopt, resopt)
raise EmptyResponseError unless env
receive_headers(env.header)
begin
@proxy.check_fault(env.body)
@ -249,21 +349,6 @@ class WSDLDriver
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_body: SOAPBasetype/SOAPCompoundtype
def document_send(name, header_obj, body_obj)
@ -275,7 +360,7 @@ class WSDLDriver
:soapaction => op_info.soapaction || @soapaction,
:decode_typemap => @wsdl_types})
env = @proxy.invoke(req_header, req_body, opt)
raise EmptyResponseError.new("Empty response.") unless env
raise EmptyResponseError unless env
if env.body.fault
raise ::SOAP::FaultError.new(env.body.fault)
end
@ -297,7 +382,7 @@ class WSDLDriver
def set_wiredump_file_base(name)
if @wiredump_file_base
@proxy.set_wiredump_file_base(@wiredump_file_base + "_#{ name }")
@proxy.set_wiredump_file_base(@wiredump_file_base + "_#{name}")
end
end
@ -326,7 +411,7 @@ class WSDLDriver
def create_method_struct(op_info, *params)
parts_names = op_info.bodyparts.collect { |part| part.name }
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
new_method = SOAPStruct.new
method.each do |key, value|
@ -356,7 +441,7 @@ class WSDLDriver
if obj.nil?
nil
else
raise RuntimeError.new("No header definition in schema.")
raise RuntimeError.new("no header definition in schema: #{obj}")
end
elsif op_info.headerparts.size == 1
part = op_info.headerparts[0]
@ -366,7 +451,7 @@ class WSDLDriver
else
header = SOAPHeader.new()
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)
header.add(part.name, ele)
end
@ -391,7 +476,7 @@ class WSDLDriver
if obj.nil?
nil
else
raise RuntimeError.new("No body found in schema.")
raise RuntimeError.new("no body found in schema")
end
elsif op_info.bodyparts.size == 1
part = op_info.bodyparts[0]
@ -400,7 +485,7 @@ class WSDLDriver
else
body = SOAPBody.new
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)
body.add(ele.elename.name, ele)
end
@ -419,39 +504,40 @@ class WSDLDriver
end
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
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
parts_names = op_info.bodyparts.collect { |part| part.name }
orgname = op_info.op_name.name
if orgname != name and orgname.capitalize == name.capitalize
add_rpc_method_interface(orgname, parts_names)
end
add_rpc_method_interface(name, parts_names)
else
raise RuntimeError.new("Unknown style: #{op_info.style}")
raise RuntimeError.new("unknown style: #{op_info.style}")
end
end
def add_rpc_method_interface(name, parts_names)
sclass = class << @host; self; end
sclass.__send__(:define_method, name, proc { |*arg|
::SOAP::Mapping.define_singleton_method(@host, name) do |*arg|
unless arg.size == parts_names.size
raise ArgumentError.new(
"wrong number of arguments (#{arg.size} for #{parts_names.size})")
end
@servant.rpc_call(name, *arg)
})
end
@host.method(name)
end
def add_document_method_interface(name)
sclass = class << @host; self; end
sclass.__send__(:define_method, name, proc { |h, b|
def add_document_method_interface(name, parts_names)
::SOAP::Mapping.define_singleton_method(@host, name) do |*arg|
@servant.document_send(name, h, b)
})
end
@host.method(name)
end
@ -476,5 +562,3 @@ end
end