diff options
author | nahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-12-20 13:50:15 +0000 |
---|---|---|
committer | nahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-12-20 13:50:15 +0000 |
commit | e5a3aba50e2bd7e0327aacd8fe1ab7963fc4a007 (patch) | |
tree | b56a6e784df74331c28e4a94901b96837f214e35 | |
parent | 643dc132113489749c333a6bda14730d9b175ed1 (diff) |
* added files:
* lib/soap/mapping/wsdl*.rb
* lib/wsdl/soap/element.rb
* lib/wsdl/xmlSchema/simpleContent.rb
* modified files:
* lib/soap/*
* lib/wsdl/*
* lib/xsd/*
* test/soap/*
* test/wsdl/*
* test/xsd/*
* summary
* imported from the soap4r repository. Version: 1.5.3-ruby1.8.2
* added several XSD basetype support: nonPositiveInteger,
negativeInteger, nonNegativeInteger, unsignedLong, unsignedInt,
unsignedShort, unsignedByte, positiveInteger
* HTTP client connection/send/receive timeout support.
* HTTP client/server gzipped content encoding support.
* improved WSDL schema definition support; still is far from
complete, but is making step by step improovement.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@7612 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
82 files changed, 2977 insertions, 1145 deletions
@@ -1,3 +1,32 @@ +Mon Dec 20 22:40:31 2004 NAKAMURA, Hiroshi <[email protected]> + + * added files: + * lib/soap/mapping/wsdl*.rb + * lib/wsdl/soap/element.rb + * lib/wsdl/xmlSchema/simpleContent.rb + + * modified files: + * lib/soap/* + * lib/wsdl/* + * lib/xsd/* + * test/soap/* + * test/wsdl/* + * test/xsd/* + + * summary + * imported from the soap4r repository. Version: 1.5.3-ruby1.8.2 + + * added several XSD basetype support: nonPositiveInteger, + negativeInteger, nonNegativeInteger, unsignedLong, unsignedInt, + unsignedShort, unsignedByte, positiveInteger + + * HTTP client connection/send/receive timeout support. + + * HTTP client/server gzipped content encoding support. + + * improved WSDL schema definition support; still is far from + complete, but is making step by step improovement. + Mon Dec 20 14:45:19 2004 GOTOU Yuuzou <[email protected]> * lib/net/https.rb: delete descriptions about key_file and cert_file. @@ -3616,7 +3645,7 @@ Mon Jul 12 21:20:51 2004 Dave Thomas <[email protected]> Sat Jul 10 09:30:24 2004 NAKAMURA, Hiroshi <[email protected]> - * test/soap/marshal/test_struct.rb: use qualified build-tin class name + * test/soap/marshal/test_struct.rb: use qualified built-in class name (::Struct) to avoid name crash. Sat Jul 10 04:21:56 2004 Hidetoshi NAGAI <[email protected]> diff --git a/lib/soap/baseData.rb b/lib/soap/baseData.rb index 49c1d2d1f4..2d3d059ebf 100644 --- a/lib/soap/baseData.rb +++ b/lib/soap/baseData.rb @@ -319,6 +319,16 @@ class SOAPInteger < XSD::XSDInteger extend SOAPModuleUtils end +class SOAPNonPositiveInteger < XSD::XSDNonPositiveInteger + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPNegativeInteger < XSD::XSDNegativeInteger + include SOAPBasetype + extend SOAPModuleUtils +end + class SOAPLong < XSD::XSDLong include SOAPBasetype extend SOAPModuleUtils @@ -334,6 +344,41 @@ class SOAPShort < XSD::XSDShort extend SOAPModuleUtils end +class SOAPByte < XSD::XSDByte + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPNonNegativeInteger < XSD::XSDNonNegativeInteger + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedLong < XSD::XSDUnsignedLong + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedInt < XSD::XSDUnsignedInt + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedShort < XSD::XSDUnsignedShort + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPUnsignedByte < XSD::XSDUnsignedByte + include SOAPBasetype + extend SOAPModuleUtils +end + +class SOAPPositiveInteger < XSD::XSDPositiveInteger + include SOAPBasetype + extend SOAPModuleUtils +end + ### ## Compound datatypes. @@ -466,6 +511,7 @@ class SOAPElement # Text interface. attr_accessor :text + alias data text # Element interfaces. def add(value) diff --git a/lib/soap/encodingstyle/literalHandler.rb b/lib/soap/encodingstyle/literalHandler.rb index 72a10b2daa..5b702785e6 100644 --- a/lib/soap/encodingstyle/literalHandler.rb +++ b/lib/soap/encodingstyle/literalHandler.rb @@ -159,7 +159,7 @@ class LiteralHandler < Handler def decode_attrs(ns, attrs) extraattr = {} attrs.each do |key, value| - qname = ns.parse(key) + qname = ns.parse_local(key) extraattr[qname] = value end extraattr diff --git a/lib/soap/encodingstyle/soapHandler.rb b/lib/soap/encodingstyle/soapHandler.rb index 114060bd02..134a2ae8b8 100644 --- a/lib/soap/encodingstyle/soapHandler.rb +++ b/lib/soap/encodingstyle/soapHandler.rb @@ -404,7 +404,7 @@ private def decode_defined_complextype(elename, typename, typedef, arytypestr) case typedef.compoundtype - when :TYPE_STRUCT + when :TYPE_STRUCT, :TYPE_MAP o = SOAPStruct.decode(elename, typename) o.definedtype = typedef return o @@ -421,6 +421,9 @@ private end o.definedtype = typedef return o + else + raise RuntimeError.new( + "Unknown kind of complexType: #{typedef.compoundtype}") end nil end @@ -537,6 +540,7 @@ private id = value next end + qname = ns.parse_local(key) extraattr[qname] = decode_attr_value(ns, qname, value) end diff --git a/lib/soap/mapping/factory.rb b/lib/soap/mapping/factory.rb index 6b9ac1eeaa..8e82ae5efd 100644 --- a/lib/soap/mapping/factory.rb +++ b/lib/soap/mapping/factory.rb @@ -27,39 +27,6 @@ class Factory # return convert_succeeded_or_not, obj end - if Object.respond_to?(:allocate) - # ruby/1.7 or later. - def create_empty_object(klass) - klass.allocate - end - else - MARSHAL_TAG = { - String => ['"', 1], - Regexp => ['/', 2], - Array => ['[', 1], - Hash => ['{', 1] - } - def create_empty_object(klass) - if klass <= Struct - name = klass.name - return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name)) - end - if MARSHAL_TAG.has_key?(klass) - tag, terminate = MARSHAL_TAG[klass] - return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate)) - end - MARSHAL_TAG.each do |k, v| - if klass < k - name = klass.name - tag, terminate = v - return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate)) - end - end - name = klass.name - ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name)) - end - end - def setiv2obj(obj, node, map) return if node.nil? if obj.is_a?(Array) @@ -129,7 +96,7 @@ class StringFactory_ < Factory end def soap2obj(obj_class, node, info, map) - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) decoded = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding, $KCODE) obj.replace(decoded) mark_unmarshalled_obj(node, obj) @@ -253,16 +220,16 @@ class ArrayFactory_ < Factory else arytype = XSD::AnyTypeName end - param = SOAPArray.new(ValueArrayName, 1, arytype) - mark_marshalled_obj(obj, param) - obj.each do |var| - param.add(Mapping._obj2soap(var, map)) + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) + obj.each do |item| + soap_obj.add(Mapping._obj2soap(item, map)) end - param + soap_obj end def soap2obj(obj_class, node, info, map) - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) node.soap2array(obj) do |elem| elem ? Mapping._soap2obj(elem, map) : nil @@ -282,12 +249,12 @@ class TypedArrayFactory_ < Factory return nil end arytype = info[:type] || info[0] - param = SOAPArray.new(ValueArrayName, 1, arytype) - mark_marshalled_obj(obj, param) + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) obj.each do |var| - param.add(Mapping._obj2soap(var, map)) + soap_obj.add(Mapping._obj2soap(var, map)) end - param + soap_obj end def soap2obj(obj_class, node, info, map) @@ -298,7 +265,7 @@ class TypedArrayFactory_ < Factory unless node.arytype == arytype return false end - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) node.soap2array(obj) do |elem| elem ? Mapping._soap2obj(elem, map) : nil @@ -310,14 +277,14 @@ end class TypedStructFactory_ < Factory def obj2soap(soap_class, obj, info, map) type = info[:type] || info[0] - param = soap_class.new(type) - mark_marshalled_obj(obj, param) + soap_obj = soap_class.new(type) + mark_marshalled_obj(obj, soap_obj) if obj.class <= SOAP::Marshallable - setiv2soap(param, obj, map) + setiv2soap(soap_obj, obj, map) else - setiv2soap(param, obj, map) + setiv2soap(soap_obj, obj, map) end - param + soap_obj end def soap2obj(obj_class, node, info, map) @@ -325,7 +292,7 @@ class TypedStructFactory_ < Factory unless node.type == type return false end - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) setiv2obj(obj, node, map) return true, obj @@ -347,16 +314,16 @@ class HashFactory_ < Factory (obj.respond_to?(:default_proc) and obj.default_proc) return nil end - param = SOAPStruct.new(MapQName) - mark_marshalled_obj(obj, param) + 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, map)) elem.add("value", Mapping._obj2soap(value, map)) # ApacheAxis allows only 'item' here. - param.add("item", elem) + soap_obj.add("item", elem) end - param + soap_obj end def soap2obj(obj_class, node, info, map) @@ -366,7 +333,7 @@ class HashFactory_ < Factory if node.class == SOAPStruct and node.key?('default') return false end - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) if node.class == SOAPStruct node.each do |key, value| diff --git a/lib/soap/mapping/mapping.rb b/lib/soap/mapping/mapping.rb index db7ea607fd..17a0d3bcf4 100644 --- a/lib/soap/mapping/mapping.rb +++ b/lib/soap/mapping/mapping.rb @@ -1,11 +1,14 @@ # SOAP4R - Ruby type mapping utility. -# Copyright (C) 2000, 2001, 2003 NAKAMURA Hiroshi <[email protected]>. +# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA Hiroshi <[email protected]>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. +require 'xsd/codegen/gensupport' + + module SOAP @@ -101,8 +104,10 @@ module Mapping def self._obj2soap(obj, registry, type = nil) if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__] SOAPReference.new(referent) + elsif registry + registry.obj2soap(obj, type) else - registry.obj2soap(obj.class, obj, type) + raise MappingError.new("No mapping registry given.") end end @@ -116,9 +121,41 @@ module Mapping return _soap2obj(target, registry) end end - return registry.soap2obj(node.class, node) + return registry.soap2obj(node) end + if Object.respond_to?(:allocate) + # ruby/1.7 or later. + def self.create_empty_object(klass) + klass.allocate + end + else + MARSHAL_TAG = { + String => ['"', 1], + Regexp => ['/', 2], + Array => ['[', 1], + Hash => ['{', 1] + } + def self.create_empty_object(klass) + if klass <= Struct + name = klass.name + return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name)) + end + if MARSHAL_TAG.has_key?(klass) + tag, terminate = MARSHAL_TAG[klass] + return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate)) + end + MARSHAL_TAG.each do |k, v| + if klass < k + name = klass.name + tag, terminate = v + return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate)) + end + end + name = klass.name + ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name)) + end + end def self.set_instance_vars(obj, values) values.each do |name, value| setter = name + "=" @@ -200,6 +237,19 @@ module Mapping end end + def self.find_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 + obj.instance_eval("@#{name}") + end + end + end + class << Mapping private def add_md_ary(md_ary, ary, indices, registry) diff --git a/lib/soap/mapping/registry.rb b/lib/soap/mapping/registry.rb index 1317d40cd6..24cd57fa71 100644 --- a/lib/soap/mapping/registry.rb +++ b/lib/soap/mapping/registry.rb @@ -64,12 +64,12 @@ end # For anyType object: SOAP::Mapping::Object not ::Object class Object; include Marshallable def initialize - @__members = [] - @__value_type = {} + @__soap_members = [] + @__soap_value_type = {} end def [](name) - if @__members.include?(name) + if @__soap_members.include?(name) self.__send__(name) else self.__send__(Object.safe_name(name)) @@ -77,39 +77,35 @@ class Object; include Marshallable end def []=(name, value) - if @__members.include?(name) + if @__soap_members.include?(name) self.__send__(name + '=', value) else self.__send__(Object.safe_name(name) + '=', value) end end - def __set_property(name, value) + def __soap_set_property(name, value) var_name = name - unless @__members.include?(name) + unless @__soap_members.include?(name) var_name = __define_attr_accessor(var_name) end - __set_property_value(var_name, value) + __soap_set_property_value(var_name, value) var_name end - def __members - @__members - end - private - def __set_property_value(name, value) + def __soap_set_property_value(name, value) org = self.__send__(name) - case @__value_type[name] + case @__soap_value_type[name] when :single self.__send__(name + '=', [org, value]) - @__value_type[name] = :multi + @__soap_value_type[name] = :multi when :multi org << value else self.__send__(name + '=', value) - @__value_type[name] = :single + @__soap_value_type[name] = :single end value end @@ -130,7 +126,7 @@ private var_name = Object.safe_name(var_name) retry end - @__members << var_name + @__soap_members << var_name var_name end @@ -147,30 +143,42 @@ class MappingError < Error; end class Registry class Map def initialize(registry) - @map = [] + @obj2soap = {} + @soap2obj = {} @registry = registry end - def obj2soap(klass, obj, type_qname = nil) - @map.each do |obj_class, soap_class, factory, info| - if klass == obj_class or - (info[:derived_class] and klass <= obj_class) + def obj2soap(obj, type_qname = nil) + klass = obj.class + if map = @obj2soap[klass] + map.each do |soap_class, factory, info| ret = factory.obj2soap(soap_class, obj, info, @registry) return ret if ret end end + ancestors = klass.ancestors[1..-3] # except itself, Object and Kernel + ancestors.each do |klass| + if map = @obj2soap[klass] + map.each do |soap_class, factory, info| + if info[:derived_class] + ret = factory.obj2soap(soap_class, obj, info, @registry) + return ret if ret + end + end + end + end nil end - def soap2obj(klass, node) - @map.each do |obj_class, soap_class, factory, info| - if klass == soap_class or - (info[:derived_class] and klass <= soap_class) + def soap2obj(node) + klass = node.class + if map = @soap2obj[klass] + map.each do |obj_class, factory, info| conv, obj = factory.soap2obj(obj_class, node, info, @registry) return true, obj if conv end end - return false + return false, nil end # Give priority to former entry. @@ -184,29 +192,21 @@ class Registry # Give priority to latter entry. def add(obj_class, soap_class, factory, info) info ||= {} - @map.unshift([obj_class, soap_class, factory, info]) + (@obj2soap[obj_class] ||= []).unshift([soap_class, factory, info]) + (@soap2obj[soap_class] ||= []).unshift([obj_class, factory, info]) end def clear - @map.clear + @obj2soap.clear + @soap2obj.clear end def find_mapped_soap_class(target_obj_class) - @map.each do |obj_class, soap_class, factory, info| - if obj_class == target_obj_class - return soap_class - end - end - nil + @obj2soap[target_obj_class][0] end def find_mapped_obj_class(target_soap_class) - @map.each do |obj_class, soap_class, factory, info| - if soap_class == target_soap_class - return obj_class - end - end - nil + @soap2obj[target_soap_class][0] end end @@ -243,6 +243,24 @@ class Registry {:derived_class => true}], [::Integer, ::SOAP::SOAPShort, BasetypeFactory, {:derived_class => true}], + [::Integer, ::SOAP::SOAPByte, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory, + {:derived_class => true}], [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory, {:derived_class => true}], [::String, ::SOAP::SOAPBase64, Base64Factory], @@ -289,6 +307,24 @@ class Registry {:derived_class => true}], [::Integer, ::SOAP::SOAPShort, BasetypeFactory, {:derived_class => true}], + [::Integer, ::SOAP::SOAPByte, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory, + {:derived_class => true}], [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory, {:derived_class => true}], [::String, ::SOAP::SOAPBase64, Base64Factory], @@ -313,6 +349,10 @@ class Registry {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}], ] + attr_accessor :default_factory + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + def initialize(config = {}) @config = config @map = Map.new(self) @@ -323,7 +363,6 @@ class Registry @allow_original_mapping = false @map.init(SOAPBaseMap) end - @allow_untyped_struct = @config.key?(:allow_untyped_struct) ? @config[:allow_untyped_struct] : true @rubytype_factory = RubytypeFactory.new( @@ -341,16 +380,16 @@ class Registry alias set add # This mapping registry ignores type hint. - def obj2soap(klass, obj, type_qname = nil) - soap = _obj2soap(klass, obj, type_qname) + def obj2soap(obj, type_qname = nil) + soap = _obj2soap(obj, type_qname) if @allow_original_mapping addextend2soap(soap, obj) end soap end - def soap2obj(klass, node) - obj = _soap2obj(klass, node) + def soap2obj(node) + obj = _soap2obj(node) if @allow_original_mapping addextend2obj(obj, node.extraattr[RubyExtendName]) addiv2obj(obj, node.extraattr[RubyIVarName]) @@ -358,18 +397,6 @@ class Registry obj end - def default_factory=(factory) - @default_factory = factory - end - - def excn_handler_obj2soap=(handler) - @excn_handler_obj2soap = handler - end - - def excn_handler_soap2obj=(handler) - @excn_handler_soap2obj = handler - end - def find_mapped_soap_class(obj_class) @map.find_mapped_soap_class(obj_class) end @@ -380,7 +407,7 @@ class Registry private - def _obj2soap(klass, obj, type_qname) + def _obj2soap(obj, type_qname) ret = nil if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray) obj.replace do |ele| @@ -391,32 +418,31 @@ private return obj end begin - ret = @map.obj2soap(klass, obj, type_qname) || - @default_factory.obj2soap(klass, obj, nil, self) + ret = @map.obj2soap(obj, type_qname) || + @default_factory.obj2soap(nil, obj, nil, self) + return ret if ret rescue MappingError end - return ret if ret if @excn_handler_obj2soap ret = @excn_handler_obj2soap.call(obj) { |yield_obj| Mapping._obj2soap(yield_obj, self) } + return ret if ret end - return ret if ret - raise MappingError.new("Cannot map #{ klass.name } to SOAP/OM.") + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") end # Might return nil as a mapping result. - def _soap2obj(klass, node) + def _soap2obj(node) if node.extraattr.key?(RubyTypeName) - conv, obj = @rubytype_factory.soap2obj(klass, node, nil, self) + conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self) return obj if conv else - conv, obj = @map.soap2obj(klass, node) + conv, obj = @map.soap2obj(node) return obj if conv - conv, obj = @default_factory.soap2obj(klass, node, nil, self) + conv, obj = @default_factory.soap2obj(nil, node, nil, self) return obj if conv end - if @excn_handler_soap2obj begin return @excn_handler_soap2obj.call(node) { |yield_node| diff --git a/lib/soap/mapping/rubytypeFactory.rb b/lib/soap/mapping/rubytypeFactory.rb index a46d93275f..6266148b55 100644 --- a/lib/soap/mapping/rubytypeFactory.rb +++ b/lib/soap/mapping/rubytypeFactory.rb @@ -318,7 +318,7 @@ private case node.type when TYPE_HASH klass = rubytype ? Mapping.class_from_name(rubytype) : Hash - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) node.each do |key, value| next unless key == 'item' @@ -330,14 +330,14 @@ private end when TYPE_REGEXP klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) source = node['source'].string options = node['options'].data || 0 Regexp.instance_method(:initialize).bind(obj).call(source, options) when TYPE_RANGE klass = rubytype ? Mapping.class_from_name(rubytype) : Range - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) first = Mapping._soap2obj(node['begin'], map) last = Mapping._soap2obj(node['end'], map) @@ -358,7 +358,7 @@ private unless klass <= ::Struct return false end - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) node['member'].each do |name, value| obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) @@ -378,7 +378,7 @@ private obj = klass.new mark_unmarshalled_obj(node, obj) node.each do |name, value| - obj.__set_property(name, Mapping._soap2obj(value, map)) + obj.__soap_set_property(name, Mapping._soap2obj(value, map)) end return true, obj else @@ -414,7 +414,7 @@ private end klass_type = Mapping.class2qname(klass) return nil unless node.type.match(klass_type) - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) setiv2obj(obj, node, map) obj @@ -423,7 +423,7 @@ private def exception2obj(klass, node, map) message = Mapping._soap2obj(node['message'], map) backtrace = Mapping._soap2obj(node['backtrace'], map) - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) obj = obj.exception(message) mark_unmarshalled_obj(node, obj) obj.set_backtrace(backtrace) @@ -433,7 +433,7 @@ private # Only creates empty array. Do String#replace it with real string. def array2obj(node, map, rubytype) klass = rubytype ? Mapping.class_from_name(rubytype) : Array - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) obj end @@ -441,7 +441,7 @@ private # Only creates empty string. Do String#replace it with real string. def string2obj(node, map, rubytype) klass = rubytype ? Mapping.class_from_name(rubytype) : String - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) obj end diff --git a/lib/soap/mapping/typeMap.rb b/lib/soap/mapping/typeMap.rb index 93f24b4bd8..34db19a5b6 100644 --- a/lib/soap/mapping/typeMap.rb +++ b/lib/soap/mapping/typeMap.rb @@ -30,9 +30,18 @@ TypeMap = { XSD::XSDAnyURI::Type => SOAPAnyURI, XSD::XSDQName::Type => SOAPQName, XSD::XSDInteger::Type => SOAPInteger, + XSD::XSDNonPositiveInteger::Type => SOAPNonPositiveInteger, + XSD::XSDNegativeInteger::Type => SOAPNegativeInteger, XSD::XSDLong::Type => SOAPLong, XSD::XSDInt::Type => SOAPInt, XSD::XSDShort::Type => SOAPShort, + XSD::XSDByte::Type => SOAPByte, + XSD::XSDNonNegativeInteger::Type => SOAPNonNegativeInteger, + XSD::XSDUnsignedLong::Type => SOAPUnsignedLong, + XSD::XSDUnsignedInt::Type => SOAPUnsignedInt, + XSD::XSDUnsignedShort::Type => SOAPUnsignedShort, + XSD::XSDUnsignedByte::Type => SOAPUnsignedByte, + XSD::XSDPositiveInteger::Type => SOAPPositiveInteger, SOAP::SOAPBase64::Type => SOAPBase64, } diff --git a/lib/soap/mapping/wsdlencodedregistry.rb b/lib/soap/mapping/wsdlencodedregistry.rb new file mode 100644 index 0000000000..4433c73cf0 --- /dev/null +++ b/lib/soap/mapping/wsdlencodedregistry.rb @@ -0,0 +1,173 @@ +# SOAP4R - WSDL encoded mapping registry. +# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <[email protected]>. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' + + +module SOAP +module Mapping + + +class WSDLEncodedRegistry + include TraverseSupport + + attr_reader :definedtypes + attr_accessor :excn_handler_obj2soap + + def initialize(definedtypes, config = {}) + @definedtypes = definedtypes + @config = config + @excn_handler_obj2soap = nil + # For mapping AnyType element. + @rubytype_factory = RubytypeFactory.new( + :allow_untyped_struct => true, + :allow_original_mapping => true + ) + end + + def obj2soap(obj, type_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) + end + return soap_obj if soap_obj + if @excn_handler_obj2soap + soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping._obj2soap(yield_obj, self) + } + return soap_obj if soap_obj + end + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") + end + + def soap2obj(node) + raise RuntimeError.new("#{ self } is for obj2soap only.") + end + +private + + def soap2soap(obj, type_qname) + if obj.is_a?(SOAPBasetype) + obj + elsif obj.is_a?(SOAPStruct) && (type = @definedtypes[type_qname]) + soap_obj = obj + mark_marshalled_obj(obj, soap_obj) + elements2soap(obj, soap_obj, type.content.elements) + soap_obj + elsif obj.is_a?(SOAPArray) && (type = @definedtypes[type_qname]) + soap_obj = obj + contenttype = type.child_type + mark_marshalled_obj(obj, soap_obj) + obj.replace do |ele| + Mapping._obj2soap(ele, self, contenttype) + end + soap_obj + else + nil + end + end + + def obj2type(obj, type) + if type.is_a?(::WSDL::XMLSchema::SimpleType) + simple2soap(obj, type) + else + complex2soap(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 + type.check_lexical_format(obj) + o + end + + def complex2soap(obj, type) + case type.compoundtype + when :TYPE_STRUCT + struct2soap(obj, type.name, type) + when :TYPE_ARRAY + array2soap(obj, type.name, type) + when :TYPE_MAP + map2soap(obj, type.name, type) + else + raise MappingError.new("Unknown compound type: #{ type.compoundtype }") + 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) + mark_marshalled_obj(obj, soap_obj) + else + soap_obj = type.new(obj) + end + soap_obj + end + + def struct2soap(obj, type_qname, type) + soap_obj = SOAPStruct.new(type_qname) + mark_marshalled_obj(obj, soap_obj) + elements2soap(obj, soap_obj, type.content.elements) + soap_obj + end + + def array2soap(obj, type_qname, type) + 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)) + end + soap_obj + end + + MapKeyName = XSD::QName.new(nil, "key") + MapValueName = XSD::QName.new(nil, "value") + def map2soap(obj, type_qname, type) + 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) + end + soap_obj + end + + def elements2soap(obj, soap_obj, elements) + elements.each do |element| + name = element.name.name + child_obj = obj.instance_eval("@#{ name }") + soap_obj.add(name, Mapping._obj2soap(child_obj, self, element.type)) + end + end +end + + +end +end diff --git a/lib/soap/mapping/wsdlliteralregistry.rb b/lib/soap/mapping/wsdlliteralregistry.rb new file mode 100644 index 0000000000..c190c9d904 --- /dev/null +++ b/lib/soap/mapping/wsdlliteralregistry.rb @@ -0,0 +1,281 @@ +# SOAP4R - WSDL literal mapping registry. +# Copyright (C) 2004 NAKAMURA, Hiroshi <[email protected]>. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' +require 'xsd/codegen/gensupport' + + +module SOAP +module Mapping + + +class WSDLLiteralRegistry + attr_reader :definedelements + attr_reader :definedtypes + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + + def initialize(definedelements = nil, definedtypes = nil) + @definedelements = definedelements + @definedtypes = definedtypes + @rubytype_factory = RubytypeFactory.new(:allow_original_mapping => false) + @schema_element_cache = {} + end + + def obj2soap(obj, qname) + ret = nil + if [email protected]? && ele = @definedelements[qname] + ret = _obj2soap(obj, ele) + elsif [email protected]? && type = @definedtypes[qname] + ret = obj2type(obj, type) + else + ret = unknownobj2soap(obj, qname) + end + return ret if ret + if @excn_handler_obj2soap + ret = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping._obj2soap(yield_obj, self) + } + return ret if ret + end + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") + end + + # node should be a SOAPElement + def soap2obj(node) + begin + return soapele2obj(node) + 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 _obj2soap(obj, ele) + o = nil + if ele.type + if type = @definedtypes[ele.type] + o = obj2type(obj, type) + elsif type = TypeMap[ele.type] + o = base2soap(obj, type) + else + 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 + else + raise MappingError.new('Illegal schema?') + end + o + end + + def obj2type(obj, type) + if type.is_a?(::WSDL::XMLSchema::SimpleType) + simple2soap(obj, type) + else + complex2soap(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 + type.check_lexical_format(obj) + o + end + + def complex2soap(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)) + 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 + elsif obj.is_a?(Hash) + ele = SOAPElement.from_obj(obj) + ele.elename = name + ele + else # expected to be a basetype or an anyType. + o = Mapping.obj2soap(obj) + o.elename = name + o + end + end + + def 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)) + 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 + 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) + else + soap_obj = type.new(obj) + end + soap_obj + end + + def anytype2obj(node) + if node.is_a?(::SOAP::SOAPBasetype) + return node.data + 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) + 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') + soapele2definedobj(node, obj_class) + elsif node.is_a?(SOAPElement) + node.to_obj + else + result, obj = @rubytype_factory.soap2obj(nil, node, nil, self) + obj + end + end + + def soapele2definedobj(node, obj_class) + obj = Mapping.create_empty_object(obj_class) + add_elements2obj(node, obj) + add_attributes2obj(node, obj) + obj + end + + def add_elements2obj(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) + 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 = soapele2obj(value, klass) + end + else + raise MappingError.new("Unknown class: #{class_name}") + end + else # untyped element is treated as anyType. + child = anytype2obj(value) + end + if as_array.include?(class_name) + (vars[name] ||= []) << child + else + vars[name] = child + end + end + Mapping.set_instance_vars(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 + 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] + end + + def schema_attribute_definition(klass) + attributes = klass.class_eval('@@schema_attribute') + end +end + + +end +end diff --git a/lib/soap/netHttpClient.rb b/lib/soap/netHttpClient.rb index 1e9d71c5a3..5b16a5b443 100644 --- a/lib/soap/netHttpClient.rb +++ b/lib/soap/netHttpClient.rb @@ -26,13 +26,17 @@ class NetHttpClient attr_accessor :debug_dev attr_accessor :ssl_config # ignored for now. attr_accessor :protocol_version # ignored for now. + attr_accessor :connect_timeout + attr_accessor :send_timeout # ignored for now. + attr_accessor :receive_timeout def initialize(proxy = nil, agent = nil) @proxy = proxy ? URI.parse(proxy) : nil @agent = agent @debug_dev = nil @session_manager = SessionManager.new - @no_proxy = nil + @no_proxy = @ssl_config = @protocol_version = nil + @connect_timeout = @send_timeout = @receive_timeout = nil end def test_loopback_response @@ -55,6 +59,7 @@ class NetHttpClient def set_basic_auth(uri, user_id, passwd) # net/http does not handle url. @basic_auth = [user_id, passwd] + raise NotImplementedError.new("basic_auth is not supported under soap4r + net/http.") end def set_cookie_store(filename) @@ -99,7 +104,7 @@ private http = create_connection(url) response = nil http.start { |worker| - response, = yield(worker) + response = yield(worker) worker.finish } @debug_dev << response.body if @debug_dev @@ -116,6 +121,8 @@ private if http.respond_to?(:set_debug_output) http.set_debug_output(@debug_dev) end + http.open_timeout = @connect_timeout if @connect_timeout + http.read_timeout = @receive_timeout if @receive_timeout case url when URI::HTTPS if SSLEnabled diff --git a/lib/soap/property.rb b/lib/soap/property.rb index 113cc64f3c..882dcc6e28 100644 --- a/lib/soap/property.rb +++ b/lib/soap/property.rb @@ -32,6 +32,8 @@ module SOAP # aaa.hhh = iii # class Property + FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError + include Enumerable module Util @@ -78,8 +80,7 @@ class Property when LINE_REGEXP key, value = $1.strip, $2.strip key = "#{key_prefix}.#{key}" unless key_prefix.empty? - key = eval("\"#{key}\"") - value = eval("\"#{value}\"") + key, value = loadstr(key), loadstr(value) self[key] = value else raise TypeError.new( @@ -190,7 +191,7 @@ protected def local_referent(key) check_lock(key) if propkey?(@store[key]) and @store[key].locked? - raise TypeError.new("cannot split any key from locked property") + raise FrozenError.new("cannot split any key from locked property") end @store[key] end @@ -199,9 +200,9 @@ protected check_lock(key) if @locked if propkey?(value) - raise TypeError.new("cannot add any key to locked property") + raise FrozenError.new("cannot add any key to locked property") elsif propkey?(@store[key]) - raise TypeError.new("cannot override any key in locked property") + raise FrozenError.new("cannot override any key in locked property") end end @store[key] = value @@ -265,7 +266,7 @@ private def check_lock(key) if @locked and (key.nil? or [email protected]?(key)) - raise TypeError.new("cannot add any key to locked property") + raise FrozenError.new("cannot add any key to locked property") end end @@ -308,6 +309,10 @@ private load(f) end end + + def loadstr(str) + str.gsub(/\\./) { |c| eval("\"#{c}\"") } + end end diff --git a/lib/soap/rpc/cgistub.rb b/lib/soap/rpc/cgistub.rb index 55437bac59..e545d53c42 100644 --- a/lib/soap/rpc/cgistub.rb +++ b/lib/soap/rpc/cgistub.rb @@ -163,16 +163,16 @@ private @response = WEBrick::HTTPResponse.new({:HTTPVersion => httpversion}) conn_data = nil begin - log(INFO) { "Received a request from '#{ @remote_user }@#{ @remote_host }'." } + @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}" } + @log.debug { "XML Request: #{conn_data.receive_string}" } conn_data = route(conn_data) - log(DEBUG) { "XML Response: #{conn_data.send_string}" } + @log.debug { "XML Response: #{conn_data.send_string}" } if conn_data.is_fault @response['Status'] = 500 end @@ -189,7 +189,7 @@ private buf = '' @response.send_response(buf) buf.sub!(/^[^\r]+\r\n/, '') # Trim status line. - log(DEBUG) { "SOAP CGI Response:\n#{ buf }" } + @log.debug { "SOAP CGI Response:\n#{ buf }" } print buf epilogue end diff --git a/lib/soap/rpc/driver.rb b/lib/soap/rpc/driver.rb index 0e59dde9be..028addc3eb 100644 --- a/lib/soap/rpc/driver.rb +++ b/lib/soap/rpc/driver.rb @@ -8,6 +8,7 @@ require 'soap/soap' require 'soap/mapping' +require 'soap/mapping/wsdlliteralregistry' require 'soap/rpc/rpc' require 'soap/rpc/proxy' require 'soap/rpc/element' @@ -43,42 +44,45 @@ class Driver __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 def httpproxy - @servant.options["protocol.http.proxy"] + options["protocol.http.proxy"] end def httpproxy=(httpproxy) - @servant.options["protocol.http.proxy"] = httpproxy + options["protocol.http.proxy"] = httpproxy end def wiredump_dev - @servant.options["protocol.http.wiredump_dev"] + options["protocol.http.wiredump_dev"] end def wiredump_dev=(wiredump_dev) - @servant.options["protocol.http.wiredump_dev"] = wiredump_dev + options["protocol.http.wiredump_dev"] = wiredump_dev end def mandatorycharset - @servant.options["protocol.mandatorycharset"] + options["protocol.mandatorycharset"] end def mandatorycharset=(mandatorycharset) - @servant.options["protocol.mandatorycharset"] = mandatorycharset + options["protocol.mandatorycharset"] = mandatorycharset end def wiredump_file_base - @servant.options["protocol.wiredump_file_base"] + options["protocol.wiredump_file_base"] end def wiredump_file_base=(wiredump_file_base) - @servant.options["protocol.wiredump_file_base"] = wiredump_file_base + options["protocol.wiredump_file_base"] = wiredump_file_base end def initialize(endpoint_url, namespace, soapaction = nil) @@ -94,32 +98,59 @@ class Driver end def inspect - "#<#{self.class}:#{@servant.streamhandler.inspect}>" + "#<#{self.class}:#{@servant.inspect}>" end - def add_method(name, *params) - add_method_with_soapaction_as(name, name, @servant.soapaction, *params) + def add_rpc_method(name, *params) + param_def = create_rpc_param_def(params) + @servant.add_rpc_method(name, @servant.soapaction, name, param_def) end - def add_method_as(name, name_as, *params) - add_method_with_soapaction_as(name, name_as, @servant.soapaction, *params) + 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) end - def add_method_with_soapaction(name, soapaction, *params) - add_method_with_soapaction_as(name, name, soapaction, *params) + 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) end - def add_method_with_soapaction_as(name, name_as, soapaction, *params) - param_def = if params.size == 1 and params[0].is_a?(Array) - params[0] - else - SOAPMethod.create_param_def(params) - end - @servant.add_method(name_as, soapaction, name, param_def) + 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) + end + + # add_method is for shortcut of typical rpc/encoded method definition. + alias add_method add_rpc_method + alias add_method_as add_rpc_method_as + 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) + 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) + 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.streamhandler.reset + @servant.reset_stream end def invoke(headers, body) @@ -132,53 +163,62 @@ class Driver private + def create_rpc_param_def(params) + if params.size == 1 and params[0].is_a?(Array) + params[0] + else + SOAPMethod.create_param_def(params) + end + end + + def create_document_param_def(name, req_qname, res_qname) + [ + ['input', name, [nil, req_qname.namespace, req_qname.name]], + ['output', name, [nil, res_qname.namespace, res_qname.name]] + ] + end + def add_rpc_method_interface(name, param_def) @servant.add_rpc_method_interface(name, param_def) end + def add_document_method_interface(name, paramname) + @servant.add_document_method_interface(name, paramname) + end + class Servant__ - attr_reader :options - attr_reader :streamhandler - attr_reader :headerhandler attr_reader :proxy + attr_reader :options + attr_accessor :soapaction def initialize(host, endpoint_url, namespace) @host = host @namespace = namespace - @mapping_registry = nil @soapaction = nil - @wiredump_file_base = nil @options = setup_options - @streamhandler = HTTPPostStreamHandler.new(endpoint_url, - @options["protocol.http"] ||= ::SOAP::Property.new) - @headerhandler = Header::HandlerSet.new - @proxy = Proxy.new(@streamhandler, @soapaction) - @proxy.allow_unqualified_element = true + @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 - @streamhandler.endpoint_url + @proxy.endpoint_url end def endpoint_url=(endpoint_url) - @streamhandler.endpoint_url = endpoint_url - @streamhandler.reset + @proxy.endpoint_url = endpoint_url end def mapping_registry - @mapping_registry + @proxy.mapping_registry end def mapping_registry=(mapping_registry) - @mapping_registry = mapping_registry - end - - def soapaction - @soapaction - end - - def soapaction=(soapaction) - @soapaction = soapaction + @proxy.mapping_registry = mapping_registry end def default_encodingstyle @@ -189,11 +229,42 @@ private @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 - @streamhandler.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? @@ -205,39 +276,25 @@ private def call(name, *params) set_wiredump_file_base(name) - # Convert parameters: params array => SOAPArray => members array - params = Mapping.obj2soap(params, @mapping_registry).to_a - env = @proxy.call(call_headers, name, *params) - raise EmptyResponseError.new("Empty response.") unless env - receive_headers(env.header) - begin - @proxy.check_fault(env.body) - rescue SOAP::FaultError => e - Mapping.fault2exception(e) - end - - 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) - } - return [ret].concat(outparams) - else - return ret - end + @proxy.call(name, *params) end - def add_method(name_as, soapaction, name, param_def) + def add_rpc_method(name_as, soapaction, name, param_def) qname = XSD::QName.new(@namespace, name_as) - @proxy.add_method(qname, soapaction, name, param_def) + @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_names = [] i = 0 - @proxy.method[name].each_param_name(RPC::SOAPMethod::IN, + @proxy.operation[name].each_param_name(RPC::SOAPMethod::IN, RPC::SOAPMethod::INOUT) do |param_name| i += 1 param_names << "arg#{ i }" @@ -251,41 +308,41 @@ private @host.method(name) end - private - - def call_headers - headers = @headerhandler.on_outbound - if headers.empty? - nil - else - h = ::SOAP::SOAPHeader.new - headers.each do |header| - h.add(header.elename.name, header) - end - h - end + def add_document_method_interface(name, paramname) + @host.instance_eval <<-EOS + def #{ name }(param) + @servant.call(#{ name.dump }, param) + end + EOS + @host.method(name) end - def receive_headers(headers) - @headerhandler.on_inbound(headers) if headers - end + private def set_wiredump_file_base(name) if @wiredump_file_base - @streamhandler.wiredump_file_base = @wiredump_file_base + "_#{ name }" + @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"] + opt = opt["client"] end opt ||= Property.new opt.add_hook("protocol.mandatorycharset") do |key, value| - @proxy.mandatorycharset = value + @proxy.mandatorycharset = value end opt.add_hook("protocol.wiredump_file_base") do |key, value| - @wiredump_file_base = value + @wiredump_file_base = value end opt["protocol.http.charset"] ||= XSD::Charset.encoding_label opt["protocol.http.proxy"] ||= Env::HTTP_PROXY diff --git a/lib/soap/rpc/httpserver.rb b/lib/soap/rpc/httpserver.rb index 7b1f961d9e..dccf950480 100644 --- a/lib/soap/rpc/httpserver.rb +++ b/lib/soap/rpc/httpserver.rb @@ -55,6 +55,8 @@ class HTTPServer < Logger::Application @soaplet.app_scope_router.mapping_registry = mapping_registry 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) @@ -72,23 +74,52 @@ class HTTPServer < Logger::Application @soaplet.add_rpc_headerhandler(obj) end - def add_method(obj, name, *param) - add_method_as(obj, name, name, *param) + # method entry interface + + def add_rpc_method(obj, name, *param) + add_rpc_method_as(obj, name, name, *param) + 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_method_as(obj, name, name_as, *param) + def add_rpc_method_as(obj, name, name_as, *param) qname = XSD::QName.new(@default_namespace, name_as) soapaction = nil - method = obj.method(name) - param_def = if param.size == 1 and param[0].is_a?(Array) - param[0] - elsif param.empty? - ::SOAP::RPC::SOAPMethod.create_param_def( - (1..method.arity.abs).collect { |i| "p#{ i }" }) - else - SOAP::RPC::SOAPMethod.create_param_def(param) - end - @soaplet.app_scope_router.add_method(obj, qname, soapaction, name, param_def) + param_def = create_param_def(obj, name, param) + add_operation(qname, soapaction, obj, 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) + 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 end private diff --git a/lib/soap/rpc/proxy.rb b/lib/soap/rpc/proxy.rb index 355bf2e81a..ca110664f9 100644 --- a/lib/soap/rpc/proxy.rb +++ b/lib/soap/rpc/proxy.rb @@ -28,73 +28,79 @@ public attr_accessor :mandatorycharset attr_accessor :allow_unqualified_element attr_accessor :default_encodingstyle - attr_reader :method + attr_accessor :generate_explicit_type + attr_reader :headerhandler + attr_reader :streamhandler - def initialize(streamhandler, soapaction = nil) - @streamhandler = streamhandler + attr_accessor :mapping_registry + attr_accessor :literal_mapping_registry + + attr_reader :operation + + def initialize(endpoint_url, soapaction, options) + @endpoint_url = endpoint_url @soapaction = soapaction - @method = {} + @options = options + @streamhandler = HTTPStreamHandler.new( + @options["protocol.http"] ||= ::SOAP::Property.new) + @operation = {} @mandatorycharset = nil - @allow_unqualified_element = false + @allow_unqualified_element = true @default_encodingstyle = nil + @generate_explicit_type = true + @headerhandler = Header::HandlerSet.new + @mapping_registry = nil + @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new end - class Request - include RPC + def inspect + "#<#{self.class}:#{@endpoint_url}>" + end - public + def endpoint_url + @endpoint_url + end - attr_reader :method - attr_reader :namespace - attr_reader :name + def endpoint_url=(endpoint_url) + @endpoint_url = endpoint_url + reset_stream + end - def initialize(model, values) - @method = model.dup - @namespace = @method.elename.namespace - @name = @method.elename.name + def reset_stream + @streamhandler.reset(@endpoint_url) + end - params = {} - - if ((values.size == 1) and (values[0].is_a?(Hash))) - params = values[0] - else - i = 0 - @method.each_param_name(SOAPMethod::IN, SOAPMethod::INOUT) do |name| - params[name] = values[i] || SOAPNil.new - i += 1 - end - end - @method.set_param(params) - end + def set_wiredump_file_base(wiredump_file_base) + @streamhandler.wiredump_file_base = wiredump_file_base end - def add_method(qname, soapaction, name, param_def) - @method[name] = SOAPMethodRequest.new(qname, param_def, soapaction) + def test_loopback_response + @streamhandler.test_loopback_response end - def create_request(name, *values) - if (@method.key?(name)) - method = @method[name] - method.encodingstyle = @default_encodingstyle if @default_encodingstyle - else - raise SOAP::RPC::MethodDefinitionError.new( - "Method: #{ name } not defined.") - end - Request.new(method, values) + def add_rpc_method(qname, soapaction, name, param_def, opt = {}) + 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) end - def invoke(req_header, req_body, soapaction = nil) - if req_header and !req_header.is_a?(SOAPHeader) - req_header = create_header(req_header) - end - if !req_body.is_a?(SOAPBody) - req_body = SOAPBody.new(req_body) - end - opt = create_options - opt[:external_content] = nil + def add_document_method(qname, 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) + end + + # add_method is for shortcut of typical rpc/encoded method definition. + alias add_method add_rpc_method + + def invoke(req_header, req_body, opt = create_options) req_env = SOAPEnvelope.new(req_header, req_body) - send_string = Processor.marshal(req_env, opt) - conn_data = StreamHandler::ConnectionData.new(send_string) + opt[:external_content] = nil + conn_data = marshal(req_env, opt) if ext = opt[:external_content] mime = MIMEMessage.new ext.each do |k, v| @@ -105,16 +111,33 @@ public conn_data.send_string = mime.content_str conn_data.send_contenttype = mime.headers['content-type'].str end - conn_data = @streamhandler.send(conn_data, soapaction) + conn_data = @streamhandler.send(@endpoint_url, conn_data, opt[:soapaction]) if conn_data.receive_string.empty? - return nil, nil + return nil end unmarshal(conn_data, opt) end - def call(req_header, name, *values) - req = create_request(name, *values) - invoke(req_header, req.method, req.method.soapaction || @soapaction) + def call(name, *params) + unless op_info = @operation[name] + raise MethodDefinitionError, "Method: #{name} not defined." + end + req_header = create_request_header + req_body = op_info.create_request_body(params, @mapping_registry, + @literal_mapping_registry) + opt = create_options({ + :soapaction => op_info.soapaction || @soapaction, + :default_encodingstyle => op_info.response_default_encodingstyle}) + env = invoke(req_header, req_body, opt) + receive_headers(env.header) + raise EmptyResponseError.new("Empty response.") unless env + begin + check_fault(env.body) + rescue ::SOAP::FaultError => e + Mapping.fault2exception(e) + end + op_info.create_response_obj(env, @mapping_registry, + @literal_mapping_registry) end def check_fault(body) @@ -125,6 +148,28 @@ public private + def create_request_header + headers = @headerhandler.on_outbound + if headers.empty? + nil + else + h = ::SOAP::SOAPHeader.new + headers.each do |header| + h.add(header.elename.name, header) + end + h + end + end + + def receive_headers(headers) + @headerhandler.on_inbound(headers) if headers + end + + def marshal(env, opt) + send_string = Processor.marshal(env, opt) + StreamHandler::ConnectionData.new(send_string) + end + def unmarshal(conn_data, opt) contenttype = conn_data.receive_contenttype if /#{MIMEMessage::MultipartContentType}/i =~ contenttype @@ -156,14 +201,115 @@ private header end - def create_options + def create_options(hash = nil) opt = {} opt[:default_encodingstyle] = @default_encodingstyle - if @allow_unqualified_element - opt[:allow_unqualified_element] = true - end + opt[:allow_unqualified_element] = @allow_unqualified_element + opt[:generate_explicit_type] = @generate_explicit_type + opt.update(hash) if hash opt end + + class Operation + attr_reader :soapaction + attr_reader :request_style + attr_reader :response_style + attr_reader :request_use + attr_reader :response_use + + def initialize(qname, soapaction, name, 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) + if @request_style == :rpc + @rpc_method_factory = SOAPMethodRequest.new(qname, param_def, + @soapaction) + else + @document_method_name = {} + 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) + else + raise MethodDefinitionError, "unknown type: " + inout + end + end + end + end + + def request_default_encodingstyle + (@request_style == :rpc) ? EncodingNamespace : LiteralNamespace + end + + def response_default_encodingstyle + (@response_style == :rpc) ? EncodingNamespace : LiteralNamespace + end + + # for rpc + def each_param_name(*target) + if @request_style == :rpc + @rpc_method_factory.each_param_name(*target) do |name| + yield(name) + end + else + yield(@document_method_name[:input].name) + 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) + 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 + else + Mapping.soap2obj(env.body.root_node, literal_mapping_registry) + end + end + + private + + ALLOWED_STYLE = [:rpc, :document] + def check_style(style) + unless ALLOWED_STYLE.include?(style) + raise MethodDefinitionError, "unknown style: " + style + end + end + end end diff --git a/lib/soap/rpc/router.rb b/lib/soap/rpc/router.rb index 9d8d1c8da6..e9147af13a 100644 --- a/lib/soap/rpc/router.rb +++ b/lib/soap/rpc/router.rb @@ -9,6 +9,7 @@ require 'soap/soap' require 'soap/processor' require 'soap/mapping' +require 'soap/mapping/wsdlliteralregistry' require 'soap/rpc/rpc' require 'soap/rpc/element' require 'soap/streamHandler' @@ -27,26 +28,48 @@ class Router attr_accessor :allow_unqualified_element attr_accessor :default_encodingstyle attr_accessor :mapping_registry + attr_accessor :literal_mapping_registry attr_reader :headerhandler def initialize(actor) @actor = actor - @receiver = {} - @method_name = {} - @method = {} @allow_unqualified_element = false @default_encodingstyle = nil @mapping_registry = nil @headerhandler = Header::HandlerSet.new + @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new + @operation = {} end - def add_method(receiver, qname, soapaction, name, param_def) - fqname = fqname(qname) - @receiver[fqname] = receiver - @method_name[fqname] = name - @method[fqname] = RPC::SOAPMethodRequest.new(qname, param_def, soapaction) + 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) + end + add_operation(qname, soapaction, receiver, name, param_def, opt) + end + + def add_operation(qname, soapaction, receiver, name, param_def, opt) + @operation[fqname(qname)] = Operation.new(qname, soapaction, receiver, + name, param_def, opt) + end + + # add_method is for shortcut of typical use="encoded" method definition. + alias add_method add_rpc_method + def route(conn_data) soap_response = nil begin @@ -55,33 +78,17 @@ class Router raise ArgumentError.new("Illegal SOAP marshal format.") end receive_headers(env.header) - soap_request = env.body.request - unless soap_request.is_a?(SOAPStruct) - raise RPCRoutingError.new("Not an RPC style.") + request = env.body.request + op = @operation[fqname(request.elename)] + unless op + raise RPCRoutingError.new("Method: #{request.elename} not supported.") end - soap_response = dispatch(soap_request) + soap_response = op.call(request, @mapping_registry, @literal_mapping_registry) rescue Exception soap_response = fault($!) conn_data.is_fault = true end - - opt = options - opt[:external_content] = nil - header = call_headers - body = SOAPBody.new(soap_response) - env = SOAPEnvelope.new(header, body) - response_string = Processor.marshal(env, opt) - conn_data.send_string = response_string - 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 - end + marshal(conn_data, op, soap_response) conn_data end @@ -118,9 +125,9 @@ private else h = ::SOAP::SOAPHeader.new headers.each do |header| - h.add(header.elename.name, header) - end - h + h.add(header.elename.name, header) + end + h end end @@ -153,32 +160,28 @@ private env end - # Create new response. - def create_response(qname, result) - name = fqname(qname) - if (@method.key?(name)) - method = @method[name] - else - raise RPCRoutingError.new("Method: #{ name } not defined.") + 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 - - soap_response = method.create_method_response - if soap_response.have_outparam? - unless result.is_a?(Array) - raise RPCRoutingError.new("Out parameter was not returned.") - end - outparams = {} - i = 1 - soap_response.each_param_name('out', 'inout') do |outparam| - outparams[outparam] = Mapping.obj2soap(result[i], @mapping_registry) - i += 1 + header = call_headers + body = SOAPBody.new(soap_response) + env = SOAPEnvelope.new(header, body) + response_string = Processor.marshal(env, response_opt) + conn_data.send_string = response_string + if ext = response_opt[:external_content] + mime = MIMEMessage.new + ext.each do |k, v| + mime.add_attachment(v.data) end - soap_response.set_outparam(outparams) - soap_response.retval = Mapping.obj2soap(result[0], @mapping_registry) - else - soap_response.retval = Mapping.obj2soap(result, @mapping_registry) + mime.add_part(conn_data.send_string + "\r\n") + mime.close + conn_data.send_string = mime.content_str + conn_data.send_contenttype = mime.headers['content-type'].str end - soap_response end # Create fault response. @@ -191,31 +194,6 @@ private Mapping.obj2soap(detail, @mapping_registry)) end - # Dispatch to defined method. - def dispatch(soap_method) - request_struct = Mapping.soap2obj(soap_method, @mapping_registry) - values = soap_method.collect { |key, value| request_struct[key] } - method = lookup(soap_method.elename, values) - unless method - raise RPCRoutingError.new( - "Method: #{ soap_method.elename } not supported.") - end - - result = method.call(*values) - create_response(soap_method.elename, result) - end - - # Method lookup - def lookup(qname, values) - name = fqname(qname) - # It may be necessary to check all part of method signature... - if @method.member?(name) - @receiver[name].method(@method_name[name].intern) - else - nil - end - end - def fqname(qname) "#{ qname.namespace }:#{ qname.name }" end @@ -228,6 +206,87 @@ private 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) + @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] + if @response_style == :rpc + @rpc_response_factory = + RPC::SOAPMethodRequest.new(qname, param_def, @soapaction) + else + outputdef = param_def.find { |inout, paramname, typeinfo| inout == "output" } + klass, nsdef, namedef = outputdef[2] + @document_response_qname = ::XSD::QName.new(nsdef, namedef) + end + end + + def call(request, mapping_registry, literal_mapping_registry) + if @request_style == :rpc + param = Mapping.soap2obj(request, mapping_registry) + result = rpc_call(request, param) + else + param = Mapping.soap2obj(request, literal_mapping_registry) + result = document_call(request, param) + end + if @response_style == :rpc + rpc_response(result, mapping_registry) + else + document_response(result, literal_mapping_registry) + end + end + + private + + def rpc_call(request, param) + unless request.is_a?(SOAPStruct) + raise RPCRoutingError.new("Not an RPC style.") + 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) + end + + def rpc_response(result, mapping_registry) + soap_response = @rpc_response_factory.create_method_response + if soap_response.have_outparam? + unless result.is_a?(Array) + raise RPCRoutingError.new("Out parameter was not returned.") + end + outparams = {} + i = 1 + soap_response.each_param_name('out', 'inout') do |outparam| + outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry) + i += 1 + end + soap_response.set_outparam(outparams) + soap_response.retval = Mapping.obj2soap(result[0], mapping_registry) + else + soap_response.retval = Mapping.obj2soap(result, mapping_registry) + end + soap_response + end + + def document_response(result, literal_mapping_registry) + literal_mapping_registry.obj2soap(result, @document_response_qname) + end + end end diff --git a/lib/soap/rpc/rpc.rb b/lib/soap/rpc/rpc.rb index 5f77b4d2e9..a48b525dbb 100644 --- a/lib/soap/rpc/rpc.rb +++ b/lib/soap/rpc/rpc.rb @@ -16,7 +16,7 @@ module RPC if obj.is_a?(Module) obj.methods - Module.methods else - obj.methods - Kernel.instance_methods(true) + obj.methods - Object.instance_methods(true) end end end diff --git a/lib/soap/rpc/soaplet.rb b/lib/soap/rpc/soaplet.rb index 0c1427acf5..8f18c53d3a 100644 --- a/lib/soap/rpc/soaplet.rb +++ b/lib/soap/rpc/soaplet.rb @@ -10,6 +10,13 @@ require 'webrick/httpservlet/abstract' require 'webrick/httpstatus' require 'soap/rpc/router' require 'soap/streamHandler' +begin + require 'stringio' + require 'zlib' +rescue LoadError + STDERR.puts "Loading stringio or zlib failed. No gzipped response support." if $DEBUG +end + module SOAP module RPC @@ -18,12 +25,18 @@ module RPC class SOAPlet < WEBrick::HTTPServlet::AbstractServlet public attr_reader :app_scope_router + attr_reader :options def initialize - @router_map = {} + @rpc_router_map = {} @app_scope_router = ::SOAP::RPC::Router.new(self.class.name) @headerhandlerfactory = [] @app_scope_headerhandler = nil + @options = {} + 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 @@ -41,7 +54,7 @@ public unless factory.respond_to?(:create) raise TypeError.new("factory must respond to 'create'") end - router = setup_request_router(namespace) + router = setup_rpc_request_router(namespace) router.factory = factory router.mapping_registry = mapping_registry end @@ -49,8 +62,8 @@ public # Add servant object which has application scope. def add_rpc_servant(obj, namespace) router = @app_scope_router - SOAPlet.add_servant_to_router(router, obj, namespace) - add_router(namespace, router) + SOAPlet.add_rpc_servant_to_router(router, obj, namespace) + add_rpc_router(namespace, router) end alias add_servant add_rpc_servant @@ -84,19 +97,26 @@ public end def do_POST(req, res) - namespace = parse_soapaction(req.meta_vars['HTTP_SOAPACTION']) - router = lookup_router(namespace) + @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 - res.body = conn_data.send_string - res['content-type'] = conn_data.send_contenttype + 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 @@ -107,6 +127,9 @@ public if res.body.is_a?(IO) res.chunked = true + @config[:Logger].debug { "SOAP response: (chunked response not logged)" } + else + @config[:Logger].debug { "SOAP response: " + res.body } end end @@ -115,8 +138,9 @@ private class RequestRouter < ::SOAP::RPC::Router attr_accessor :factory - def initialize(namespace = nil) + def initialize(style = :rpc, namespace = nil) super(namespace) + @style = style @namespace = namespace @factory = nil end @@ -125,19 +149,23 @@ private obj = @factory.create namespace = self.actor router = ::SOAP::RPC::Router.new(@namespace) - SOAPlet.add_servant_to_router(router, obj, namespace) + if @style == :rpc + SOAPlet.add_rpc_servant_to_router(router, obj, namespace) + else + raise RuntimeError.new("'document' style not supported.") + end router.route(soap_string) end end - def setup_request_router(namespace) - router = @router_map[namespace] || RequestRouter.new(namespace) - add_router(namespace, router) + def setup_rpc_request_router(namespace) + router = @rpc_router_map[namespace] || RequestRouter.new(:rpc, namespace) + add_rpc_router(namespace, router) router end - def add_router(namespace, router) - @router_map[namespace] = router + def add_rpc_router(namespace, router) + @rpc_router_map[namespace] = router end def parse_soapaction(soapaction) @@ -152,7 +180,7 @@ private def lookup_router(namespace) if namespace - @router_map[namespace] || @app_scope_router + @rpc_router_map[namespace] || @app_scope_router else @app_scope_router end @@ -172,25 +200,49 @@ private end end + def encode_gzip(req, outstring) + unless encode_gzip?(req) + return nil + end + begin + ostream = StringIO.new + gz = Zlib::GzipWriter.new(ostream) + gz.write(outstring) + ostream.string + ensure + gz.close + end + end + + def encode_gzip?(req) + @options[:allow_content_encoding_gzip] and defined?(::Zlib) and + req['accept-encoding'] and + req['accept-encoding'].split(/,\s*/).include?('gzip') + end + class << self public - def add_servant_to_router(router, obj, namespace) + def add_rpc_servant_to_router(router, obj, namespace) ::SOAP::RPC.defined_methods(obj).each do |name| begin - add_servant_method_to_router(router, obj, namespace, name) + add_rpc_servant_method_to_router(router, obj, namespace, name) rescue SOAP::RPC::MethodDefinitionError => e p e if $DEBUG end end end - def add_servant_method_to_router(router, obj, namespace, name) + 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 }" }) - router.add_method(obj, qname, soapaction, name, param_def) + 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 diff --git a/lib/soap/soap.rb b/lib/soap/soap.rb index 02b26e4246..66ef1454a3 100644 --- a/lib/soap/soap.rb +++ b/lib/soap/soap.rb @@ -13,7 +13,7 @@ require 'xsd/charset' module SOAP -Version = '1.5.2' +Version = '1.5.3-ruby1.8.2' PropertyName = 'soap/property' EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/' diff --git a/lib/soap/streamHandler.rb b/lib/soap/streamHandler.rb index efadf21e07..ac2f54d5c9 100644 --- a/lib/soap/streamHandler.rb +++ b/lib/soap/streamHandler.rb @@ -8,6 +8,12 @@ require 'soap/soap' require 'soap/property' +begin + require 'stringio' + require 'zlib' +rescue LoadError + STDERR.puts "Loading stringio or zlib failed. No gzipped response support." if $DEBUG +end module SOAP @@ -44,12 +50,6 @@ class StreamHandler end end - attr_accessor :endpoint_url - - def initialize(endpoint_url) - @endpoint_url = endpoint_url - end - def self.parse_media_type(str) if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str return nil @@ -65,7 +65,7 @@ class StreamHandler end -class HTTPPostStreamHandler < StreamHandler +class HTTPStreamHandler < StreamHandler include SOAP public @@ -75,31 +75,40 @@ public NofRetry = 10 # [times] - def initialize(endpoint_url, options) - super(endpoint_url) + def initialize(options) + 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 @cookie_store = nil + @accept_encoding_gzip = false end def test_loopback_response @client.test_loopback_response end + def accept_encoding_gzip=(allow) + @accept_encoding_gzip = allow + end + def inspect - "#<#{self.class}:#{endpoint_url}>" + "#<#{self.class}>" end - def send(conn_data, soapaction = nil, charset = @charset) - send_post(conn_data, soapaction, charset) + def send(endpoint_url, conn_data, soapaction = nil, charset = @charset) + send_post(endpoint_url, conn_data, soapaction, charset) end - def reset - @client.reset(@endpoint_url) + def reset(endpoint_url = nil) + if endpoint_url.nil? + @client.reset_all + else + @client.reset(endpoint_url) + end @client.save_cookie_store if @cookie_store end @@ -143,6 +152,15 @@ private basic_auth.add_hook do |key, value| set_basic_auth(basic_auth) end + @options.add_hook("connect_timeout") do |key, value| + @client.connect_timeout = value + end + @options.add_hook("send_timeout") do |key, value| + @client.send_timeout = value + end + @options.add_hook("receive_timeout") do |key, value| + @client.receive_timeout = value + end @options.lock(true) ssl_config.unlock basic_auth.unlock @@ -213,7 +231,7 @@ private OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read }) end - def send_post(conn_data, soapaction, charset) + def send_post(endpoint_url, conn_data, soapaction, charset) conn_data.send_contenttype ||= StreamHandler.create_media_type(charset) if @wiredump_file_base @@ -226,26 +244,23 @@ private extra = {} extra['Content-Type'] = conn_data.send_contenttype extra['SOAPAction'] = "\"#{ 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 begin - res = @client.post(@endpoint_url, send_string, extra) + res = @client.post(endpoint_url, send_string, extra) rescue - @client.reset(@endpoint_url) + @client.reset(endpoint_url) raise end @wiredump_dev << "\n\n" if @wiredump_dev - receive_string = res.content - if @wiredump_file_base filename = @wiredump_file_base + '_response.xml' f = File.open(filename, "w") f << receive_string f.close end - case res.status when 405 raise PostUnavailableError.new("#{ res.status }: #{ res.reason }") @@ -254,13 +269,30 @@ private else raise HTTPStreamError.new("#{ res.status }: #{ res.reason }") end - + if res.respond_to?(:header) and !res.header['content-encoding'].empty? and + res.header['content-encoding'][0].downcase == 'gzip' + receive_string = decode_gzip(receive_string) + end conn_data.receive_string = receive_string conn_data.receive_contenttype = res.contenttype conn_data end - CRLF = "\r\n" + def send_accept_encoding_gzip? + @accept_encoding_gzip and defined?(::Zlib) + end + + def decode_gzip(instring) + unless send_accept_encoding_gzip? + raise HTTPStreamError.new("Gzipped response content.") + end + begin + gz = Zlib::GzipReader.new(StringIO.new(instring)) + gz.read + ensure + gz.close + end + end end diff --git a/lib/soap/wsdlDriver.rb b/lib/soap/wsdlDriver.rb index af868ea886..aa822df4e4 100644 --- a/lib/soap/wsdlDriver.rb +++ b/lib/soap/wsdlDriver.rb @@ -14,12 +14,14 @@ require 'soap/baseData' require 'soap/streamHandler' require 'soap/mimemessage' require 'soap/mapping' -require 'soap/mapping/wsdlRegistry' +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 'logger' +require 'xsd/codegen/gensupport' module SOAP @@ -94,13 +96,14 @@ class WSDLDriver __attr_proxy :options __attr_proxy :headerhandler + __attr_proxy :streamhandler __attr_proxy :test_loopback_response __attr_proxy :endpoint_url, true __attr_proxy :mapping_registry, true # for RPC unmarshal __attr_proxy :wsdl_mapping_registry, true # for RPC marshal __attr_proxy :default_encodingstyle, true - __attr_proxy :allow_unqualified_element, true __attr_proxy :generate_explicit_type, true + __attr_proxy :allow_unqualified_element, true def httpproxy @servant.options["protocol.http.proxy"] @@ -143,105 +146,105 @@ class WSDLDriver end def reset_stream - @servant.streamhandler.reset + @servant.reset_stream end # Backward compatibility. alias generateEncodeType= generate_explicit_type= class Servant__ - include Logger::Severity include SOAP attr_reader :options - attr_reader :streamhandler - attr_reader :headerhandler attr_reader :port - attr_accessor :mapping_registry - attr_accessor :wsdl_mapping_registry + attr_accessor :soapaction attr_accessor :default_encodingstyle attr_accessor :allow_unqualified_element attr_accessor :generate_explicit_type + attr_accessor :mapping_registry + attr_accessor :wsdl_mapping_registry def initialize(host, wsdl, port, logdev) @host = host @wsdl = wsdl @port = port @logdev = logdev - + @soapaction = nil @options = setup_options + @default_encodingstyle = nil + @allow_unqualified_element = nil + @generate_explicit_type = false @mapping_registry = nil # for rpc unmarshal @wsdl_mapping_registry = nil # for rpc marshal - @default_encodingstyle = EncodingNamespace - @allow_unqualified_element = true - @generate_explicit_type = false @wiredump_file_base = nil @mandatorycharset = nil - @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) - @wsdl_mapping_registry = Mapping::WSDLRegistry.new(@rpc_decode_typemap) - @doc_mapper = Mapper.new(@wsdl_elements, @wsdl_types) + @wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(@rpc_decode_typemap) + @doc_mapper = Mapping::WSDLLiteralRegistry.new(@wsdl_elements, @wsdl_types) endpoint_url = @port.soap_address.location - @streamhandler = HTTPPostStreamHandler.new(endpoint_url, - @options["protocol.http"] ||= Property.new) - @headerhandler = Header::HandlerSet.new # Convert a map which key is QName, to a Hash which key is String. - @operations = {} + @operation = {} @port.inputoperation_map.each do |op_name, op_info| - @operations[op_name.name] = op_info + @operation[op_name.name] = op_info add_method_interface(op_info) end + @proxy = ::SOAP::RPC::Proxy.new(endpoint_url, @soapaction, @options) + end + + def inspect + "#<#{self.class}:#{@proxy.inspect}>" end def endpoint_url - @streamhandler.endpoint_url + @proxy.endpoint_url end def endpoint_url=(endpoint_url) - @streamhandler.endpoint_url = endpoint_url - @streamhandler.reset + @proxy.endpoint_url = endpoint_url end - def test_loopback_response - @streamhandler.test_loopback_response + def headerhandler + @proxy.headerhandler end - def rpc_send(method_name, *params) - log(INFO) { "call: calling method '#{ method_name }'." } - log(DEBUG) { "call: parameters '#{ params.inspect }'." } + def streamhandler + @proxy.streamhandler + end - op_info = @operations[method_name] - method = create_method_struct(op_info, params) - req_header = call_headers - req_body = SOAPBody.new(method) - req_env = SOAPEnvelope.new(req_header, req_body) + def test_loopback_response + @proxy.test_loopback_response + end - if @wiredump_file_base - @streamhandler.wiredump_file_base = - @wiredump_file_base + '_' << method_name - end + def reset_stream + @proxy.reset_stream + end + def rpc_call(name, *values) + set_wiredump_file_base(name) + unless op_info = @operation[name] + raise MethodDefinitionError, "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, + :decode_typemap => @rpc_decode_typemap}) + env = @proxy.invoke(req_header, req_body, opt) + raise EmptyResponseError.new("Empty response.") unless env + receive_headers(env.header) begin - opt = create_options - opt[:decode_typemap] = @rpc_decode_typemap - res_env = invoke(req_env, op_info, opt) - receive_headers(res_env.header) - if res_env.body.fault - raise ::SOAP::FaultError.new(res_env.body.fault) - end + @proxy.check_fault(env.body) rescue ::SOAP::FaultError => e Mapping.fault2exception(e) end - - ret = res_env.body.response ? - Mapping.soap2obj(res_env.body.response, @mapping_registry) : nil - - if res_env.body.outparams - outparams = res_env.body.outparams.collect { |outparam| + 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) } return [ret].concat(outparams) @@ -250,32 +253,64 @@ 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) - log(INFO) { "document_send: sending document '#{ name }'." } - op_info = @operations[name] + 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) - req_env = SOAPEnvelope.new(req_header, req_body) - opt = create_options - res_env = invoke(req_env, op_info, opt) - if res_env.body.fault - raise ::SOAP::FaultError.new(res_env.body.fault) - end - res_body_obj = res_env.body.response ? - Mapping.soap2obj(res_env.body.response, @mapping_registry) : nil - return res_env.header, res_body_obj + opt = create_options({ + :soapaction => op_info.soapaction || @soapaction, + :decode_typemap => @wsdl_types}) + env = @proxy.invoke(req_header, req_body, opt) + 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 private - def call_headers - headers = @headerhandler.on_outbound + def create_options(hash = nil) + opt = {} + opt[:default_encodingstyle] = @default_encodingstyle + opt[:allow_unqualified_element] = @allow_unqualified_element + opt[:generate_explicit_type] = @generate_explicit_type + opt.update(hash) if hash + opt + end + + def set_wiredump_file_base(name) + if @wiredump_file_base + @proxy.set_wiredump_file_base(@wiredump_file_base + "_#{ name }") + end + end + + def create_request_header + headers = @proxy.headerhandler.on_outbound if headers.empty? nil else - h = ::SOAP::SOAPHeader.new + h = SOAPHeader.new headers.each do |header| h.add(header.elename.name, header) end @@ -284,10 +319,15 @@ class WSDLDriver end def receive_headers(headers) - @headerhandler.on_inbound(headers) if headers + @proxy.headerhandler.on_inbound(headers) if headers + end + + def create_request_body(op_info, *values) + method = create_method_struct(op_info, *values) + SOAPBody.new(method) end - def create_method_struct(op_info, params) + 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) @@ -313,52 +353,6 @@ class WSDLDriver o end - def invoke(req_env, op_info, opt) - opt[:external_content] = nil - send_string = Processor.marshal(req_env, opt) - log(DEBUG) { "invoke: sending string #{ send_string }" } - conn_data = StreamHandler::ConnectionData.new(send_string) - 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 - end - conn_data = @streamhandler.send(conn_data, op_info.soapaction) - log(DEBUG) { "invoke: received string #{ conn_data.receive_string }" } - if conn_data.receive_string.empty? - return nil, nil - end - unmarshal(conn_data, opt) - end - - def unmarshal(conn_data, opt) - contenttype = conn_data.receive_contenttype - if /#{MIMEMessage::MultipartContentType}/i =~ contenttype - opt[:external_content] = {} - mime = MIMEMessage.parse("Content-Type: " + contenttype, - conn_data.receive_string) - mime.parts.each do |part| - value = Attachment.new(part.content) - value.contentid = part.contentid - obj = SOAPAttachment.new(value) - opt[:external_content][value.contentid] = obj if value.contentid - end - opt[:charset] = @mandatorycharset || - StreamHandler.parse_media_type(mime.root.headers['content-type'].str) - env = Processor.unmarshal(mime.root.content, opt) - else - opt[:charset] = @mandatorycharset || - ::SOAP::StreamHandler.parse_media_type(contenttype) - env = Processor.unmarshal(conn_data.receive_string, opt) - end - env - end - def header_from_obj(obj, op_info) if obj.is_a?(SOAPHeader) obj @@ -376,7 +370,7 @@ class WSDLDriver else header = SOAPHeader.new() op_info.headerparts.each do |part| - child = Mapper.find_attribute(obj, part.name) + child = Mapping.find_attribute(obj, part.name) ele = headeritem_from_obj(child, part.element || part.eletype) header.add(part.name, ele) end @@ -390,7 +384,7 @@ class WSDLDriver elsif obj.is_a?(SOAPHeaderItem) obj else - @doc_mapper.obj2ele(obj, name) + @doc_mapper.obj2soap(obj, name) end end @@ -410,7 +404,7 @@ class WSDLDriver else body = SOAPBody.new op_info.bodyparts.each do |part| - child = Mapper.find_attribute(obj, part.name) + child = Mapping.find_attribute(obj, part.name) ele = bodyitem_from_obj(child, part.element || part.type) body.add(ele.elename.name, ele) end @@ -424,51 +418,40 @@ class WSDLDriver elsif obj.is_a?(SOAPElement) obj else - @doc_mapper.obj2ele(obj, name) + @doc_mapper.obj2soap(obj, name) end end def add_method_interface(op_info) + name = ::XSD::CodeGen::GenSupport.safemethodname(op_info.op_name.name) case op_info.style when :document - add_document_method_interface(op_info.op_name.name) + add_document_method_interface(name) when :rpc parts_names = op_info.bodyparts.collect { |part| part.name } - add_rpc_method_interface(op_info.op_name.name, parts_names) + add_rpc_method_interface(name, parts_names) else raise RuntimeError.new("Unknown style: #{op_info.style}") end end - def add_document_method_interface(name) - @host.instance_eval <<-EOS - def #{ name }(headers, body) - @servant.document_send(#{ name.dump }, headers, body) - end - EOS - end - def add_rpc_method_interface(name, parts_names) i = 0 param_names = parts_names.collect { |orgname| i += 1; "arg#{ i }" } callparam = (param_names.collect { |pname| ", " + pname }).join @host.instance_eval <<-EOS def #{ name }(#{ param_names.join(", ") }) - @servant.rpc_send(#{ name.dump }#{ callparam }) + @servant.rpc_call(#{ name.dump }#{ callparam }) end EOS end - def create_options - opt = {} - opt[:default_encodingstyle] = @default_encodingstyle - opt[:allow_unqualified_element] = @allow_unqualified_element - opt[:generate_explicit_type] = @generate_explicit_type - opt - end - - def log(sev) - @logdev.add(sev, nil, self.class) { yield } if @logdev + def add_document_method_interface(name) + @host.instance_eval <<-EOS + def #{ name }(h, b) + @servant.document_send(#{ name.dump }, h, b) + end + EOS end def setup_options @@ -487,108 +470,6 @@ class WSDLDriver opt["protocol.http.no_proxy"] ||= Env::NO_PROXY opt end - - class MappingError < StandardError; end - class Mapper - def initialize(elements, types) - @elements = elements - @types = types - end - - def obj2ele(obj, name) - if ele = @elements[name] - _obj2ele(obj, ele) - elsif type = @types[name] - obj2type(obj, type) - else - raise MappingError.new("Cannot find name #{name} in schema.") - end - end - - def ele2obj(ele, *arg) - raise NotImplementedError.new - end - - def Mapper.find_attribute(obj, attr_name) - if obj.respond_to?(attr_name) - obj.__send__(attr_name) - elsif obj.is_a?(Hash) - obj[attr_name] || obj[attr_name.intern] - else - obj.instance_eval("@#{ attr_name }") - end - end - - private - - def _obj2ele(obj, ele) - o = nil - if ele.type - if type = @types[ele.type] - o = obj2type(obj, type) - elsif type = TypeMap[ele.type] - o = base2soap(obj, type) - else - 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(_obj2ele(Mapper.find_attribute(obj, child_ele.name.name), - child_ele)) - end - else - raise MappingError.new("Illegal schema?") - end - o - end - - def obj2type(obj, type) - if type.is_a?(::WSDL::XMLSchema::SimpleType) - simple2soap(obj, type) - else - complex2soap(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 - if type.restriction.enumeration.include?(o) - raise MappingError.new("#{o} is not allowed for #{type.name}") - end - o - end - - def complex2soap(obj, type) - o = SOAPElement.new(type.name) - type.each_element do |child_ele| - o.add(_obj2ele(Mapper.find_attribute(obj, child_ele.name.name), - child_ele)) - end - o - end - - def _ele2obj(ele) - raise NotImplementedError.new - 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) - else - soap_obj = type.new(obj) - end - soap_obj - end - end end end diff --git a/lib/wsdl/binding.rb b/lib/wsdl/binding.rb index e8c9d5be9d..58a21d820d 100644 --- a/lib/wsdl/binding.rb +++ b/lib/wsdl/binding.rb @@ -52,7 +52,7 @@ class Binding < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) when TypeAttrName @type = value else diff --git a/lib/wsdl/definitions.rb b/lib/wsdl/definitions.rb index c530220fde..a7c71f2a93 100644 --- a/lib/wsdl/definitions.rb +++ b/lib/wsdl/definitions.rb @@ -216,9 +216,9 @@ class Definitions < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(@targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) when TargetNamespaceAttrName - self.targetnamespace = value + self.targetnamespace = value.source else nil end diff --git a/lib/wsdl/import.rb b/lib/wsdl/import.rb index ab244f6ca6..706cb95fe2 100644 --- a/lib/wsdl/import.rb +++ b/lib/wsdl/import.rb @@ -39,13 +39,13 @@ class Import < Info def parse_attr(attr, value) case attr when NamespaceAttrName - @namespace = value + @namespace = value.source if @content @content.targetnamespace = @namespace end @namespace when LocationAttrName - @location = value + @location = value.source @content = import(@location) if @content.is_a?(Definitions) @content.root = root diff --git a/lib/wsdl/importer.rb b/lib/wsdl/importer.rb index fac02b51a0..873be710b5 100644 --- a/lib/wsdl/importer.rb +++ b/lib/wsdl/importer.rb @@ -25,6 +25,7 @@ class Importer end def import(location) + STDERR.puts("importing: #{location}") if $DEBUG content = nil if FileTest.exist?(location) content = File.open(location).read @@ -42,12 +43,8 @@ class Importer begin WSDL::Parser.new(opt).parse(content) rescue WSDL::Parser::ParseError => orgexcn - begin - require 'wsdl/xmlSchema/parser' - WSDL::XMLSchema::Parser.new(opt).parse(content) - rescue - raise orgexcn - end + require 'wsdl/xmlSchema/parser' + WSDL::XMLSchema::Parser.new(opt).parse(content) end end diff --git a/lib/wsdl/message.rb b/lib/wsdl/message.rb index a346708cf4..cecc602da3 100644 --- a/lib/wsdl/message.rb +++ b/lib/wsdl/message.rb @@ -43,7 +43,7 @@ class Message < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(parent.targetnamespace, value) + @name = XSD::QName.new(parent.targetnamespace, value.source) else nil end diff --git a/lib/wsdl/operation.rb b/lib/wsdl/operation.rb index be28446d34..3c1f66859f 100644 --- a/lib/wsdl/operation.rb +++ b/lib/wsdl/operation.rb @@ -95,11 +95,11 @@ class Operation < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) when TypeAttrName @type = value when ParameterOrderAttrName - @parameter_order = value.split(/\s+/) + @parameter_order = value.source.split(/\s+/) else nil end diff --git a/lib/wsdl/operationBinding.rb b/lib/wsdl/operationBinding.rb index 4c04a884ea..fb44eb9660 100644 --- a/lib/wsdl/operationBinding.rb +++ b/lib/wsdl/operationBinding.rb @@ -69,7 +69,7 @@ class OperationBinding < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) else nil end diff --git a/lib/wsdl/param.rb b/lib/wsdl/param.rb index 06dd3beb7e..581ecbd8d3 100644 --- a/lib/wsdl/param.rb +++ b/lib/wsdl/param.rb @@ -63,7 +63,7 @@ class Param < Info when MessageAttrName @message = value when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) else nil end diff --git a/lib/wsdl/parser.rb b/lib/wsdl/parser.rb index 6387911f79..417ea20b47 100644 --- a/lib/wsdl/parser.rb +++ b/lib/wsdl/parser.rb @@ -123,25 +123,11 @@ private o.parent = parent if o.parent.nil? end attrs.each do |key, value| - attr = unless /:/ =~ key - XSD::QName.new(nil, key) - else - ns.parse(key) - end - value_ele = if /:/ !~ value - value - elsif /^http:\/\// =~ value # ToDo: ugly. - value - else - begin - ns.parse(value) - rescue - value - end - end - unless o.parse_attr(attr, value_ele) - STDERR.puts("Unknown attr #{ attr }.") - # raise UnknownAttributeError.new("Unknown attr #{ attr }.") + attr_ele = ns.parse(key, true) + value_ele = ns.parse(value, true) + value_ele.source = value # for recovery; value may not be a QName + unless o.parse_attr(attr_ele, value_ele) + STDERR.puts("Unknown attr #{ attr_ele }.") end end o diff --git a/lib/wsdl/part.rb b/lib/wsdl/part.rb index 30f71f15d9..5dafd4ee73 100644 --- a/lib/wsdl/part.rb +++ b/lib/wsdl/part.rb @@ -37,7 +37,7 @@ class Part < Info def parse_attr(attr, value) case attr when NameAttrName - @name = value + @name = value.source when ElementAttrName @element = value when TypeAttrName diff --git a/lib/wsdl/port.rb b/lib/wsdl/port.rb index e6553f1287..15ba86ad7c 100644 --- a/lib/wsdl/port.rb +++ b/lib/wsdl/port.rb @@ -71,7 +71,7 @@ class Port < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) when BindingAttrName @binding = value else diff --git a/lib/wsdl/portType.rb b/lib/wsdl/portType.rb index e3cf9b51ec..86893ba039 100644 --- a/lib/wsdl/portType.rb +++ b/lib/wsdl/portType.rb @@ -61,7 +61,7 @@ class PortType < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) else nil end diff --git a/lib/wsdl/service.rb b/lib/wsdl/service.rb index 0e0843a098..652b127331 100644 --- a/lib/wsdl/service.rb +++ b/lib/wsdl/service.rb @@ -50,7 +50,7 @@ class Service < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) else nil end diff --git a/lib/wsdl/soap/address.rb b/lib/wsdl/soap/address.rb index e4558e4ff8..0b2b59caf0 100644 --- a/lib/wsdl/soap/address.rb +++ b/lib/wsdl/soap/address.rb @@ -28,7 +28,7 @@ class Address < Info def parse_attr(attr, value) case attr when LocationAttrName - @location = value + @location = value.source else nil end diff --git a/lib/wsdl/soap/binding.rb b/lib/wsdl/soap/binding.rb index 1cfe9b9cc4..7e15a99701 100644 --- a/lib/wsdl/soap/binding.rb +++ b/lib/wsdl/soap/binding.rb @@ -30,13 +30,14 @@ class Binding < Info def parse_attr(attr, value) case attr when StyleAttrName - if ["document", "rpc"].include?(value) - @style = value.intern + if ["document", "rpc"].include?(value.source) + @style = value.source.intern else - raise AttributeConstraintError.new("Unexpected value #{ value }.") + raise Parser::AttributeConstraintError.new( + "Unexpected value #{ value }.") end when TransportAttrName - @transport = value + @transport = value.source else nil end diff --git a/lib/wsdl/soap/body.rb b/lib/wsdl/soap/body.rb index 47de6b1e1a..47e99f381b 100644 --- a/lib/wsdl/soap/body.rb +++ b/lib/wsdl/soap/body.rb @@ -34,13 +34,13 @@ class Body < Info def parse_attr(attr, value) case attr when PartsAttrName - @parts = value + @parts = value.source when UseAttrName - @use = value + @use = value.source when EncodingStyleAttrName - @encodingstyle = value + @encodingstyle = value.source when NamespaceAttrName - @namespace = value + @namespace = value.source else nil end diff --git a/lib/wsdl/soap/cgiStubCreator.rb b/lib/wsdl/soap/cgiStubCreator.rb index e5b64336e7..68ecfaf0a4 100644 --- a/lib/wsdl/soap/cgiStubCreator.rb +++ b/lib/wsdl/soap/cgiStubCreator.rb @@ -55,8 +55,13 @@ Methods = [ <<-EOD super(*arg) servant = #{class_name}.new - #{class_name}::Methods.each do |name_as, name, params, soapaction, ns| - add_method_with_namespace_as(ns, servant, name, name_as, params, soapaction) + #{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style| + qname = XSD::QName.new(namespace, name_as) + if style == :document + @router.add_document_method(servant, qname, soapaction, name, param_def) + else + @router.add_rpc_method(servant, qname, soapaction, name, param_def) + end end self.mapping_registry = #{class_name}::MappingRegistry self.level = Logger::Severity::ERROR diff --git a/lib/wsdl/soap/classDefCreator.rb b/lib/wsdl/soap/classDefCreator.rb index 6c7d381932..13f7802b72 100644 --- a/lib/wsdl/soap/classDefCreator.rb +++ b/lib/wsdl/soap/classDefCreator.rb @@ -19,42 +19,71 @@ class ClassDefCreator include ClassDefCreatorSupport def initialize(definitions) + @elements = definitions.collect_elements @simpletypes = definitions.collect_simpletypes @complextypes = definitions.collect_complextypes - @faulttypes = definitions.collect_faulttypes + @faulttypes = definitions.collect_faulttypes if definitions.respond_to?(:collect_faulttypes) end def dump(class_name = nil) - result = "" + result = '' if class_name result = dump_classdef(class_name) else - @complextypes.each do |type| - case type.compoundtype - when :TYPE_STRUCT - result << dump_classdef(type) - when :TYPE_ARRAY - result << dump_arraydef(type) - else - raise RuntimeError.new("Unknown complexContent definition...") - end - result << "\n" + str = dump_element + unless str.empty? + result << "\n" unless result.empty? + result << str + end + str = dump_complextype + unless str.empty? + result << "\n" unless result.empty? + result << str + end + str = dump_simpletype + unless str.empty? + result << "\n" unless result.empty? + result << str end - - result << @simpletypes.collect { |type| - dump_simpletypedef(type) - }.join("\n") end result end private + def dump_element + @elements.collect { |ele| + ele.local_complextype ? dump_classdef(ele) : '' + }.join("\n") + end + + def dump_simpletype + @simpletypes.collect { |type| + dump_simpletypedef(type) + }.join("\n") + end + + def dump_complextype + @complextypes.collect { |type| + case type.compoundtype + when :TYPE_STRUCT + dump_classdef(type) + when :TYPE_ARRAY + dump_arraydef(type) + when :TYPE_SIMPLE + STDERR.puts("not implemented: ToDo") + else + raise RuntimeError.new( + "Unknown kind of complexContent: #{type.compoundtype}") + end + }.join("\n") + end + def dump_simpletypedef(simpletype) qname = simpletype.name if simpletype.restriction.enumeration.empty? STDERR.puts("#{qname}: simpleType which is not enum type not supported.") - return "" + return '' end c = XSD::CodeGen::ModuleDef.new(create_class_name(qname)) c.comment = "#{ qname.namespace }" @@ -64,45 +93,98 @@ private c.dump end - def dump_classdef(complextype) - qname = complextype.name - if @faulttypes.index(qname) + def dump_classdef(type_or_element) + qname = type_or_element.name + if @faulttypes and @faulttypes.index(qname) c = XSD::CodeGen::ClassDef.new(create_class_name(qname), - "::StandardError") + '::StandardError') else c = XSD::CodeGen::ClassDef.new(create_class_name(qname)) end c.comment = "#{ qname.namespace }" - c.def_classvar("schema_type", qname.name.dump) - c.def_classvar("schema_ns", qname.namespace.dump) - init_lines = "" + c.def_classvar('schema_type', qname.name.dump) + c.def_classvar('schema_ns', qname.namespace.dump) + schema_attribute = [] + schema_element = [] + init_lines = '' params = [] - complextype.each_element do |element| + type_or_element.each_element do |element| + next unless element.name name = element.name.name + if element.type == XSD::AnyTypeName + type = nil + elsif basetype = basetype_class(element.type) + type = basetype.name + else + type = create_class_name(element.type) + end + attrname = safemethodname?(name) ? name : safemethodname(name) varname = safevarname(name) - c.def_attr(name, true, varname) + c.def_attr(attrname, true, varname) init_lines << "@#{ varname } = #{ varname }\n" - params << "#{ varname } = nil" + if element.map_as_array? + params << "#{ varname } = []" + type << '[]' + else + params << "#{ varname } = nil" + end + schema_element << [name, type] end - complextype.attributes.each do |attribute| - name = "attr_" + attribute.name - varname = safevarname(name) - c.def_attr(name, true, varname) - init_lines << "@#{ varname } = #{ varname }\n" - params << "#{ varname } = nil" + unless type_or_element.attributes.empty? + type_or_element.attributes.each do |attribute| + name = attribute.name.name + if basetype = basetype_class(attribute.type) + type = basetype_class(attribute.type).name + else + type = nil + end + varname = safevarname('attr_' + name) + c.def_method(varname) do <<-__EOD__ + @__soap_attribute[#{name.dump}] + __EOD__ + end + c.def_method(varname + '=', 'value') do <<-__EOD__ + @__soap_attribute[#{name.dump}] = value + __EOD__ + end + schema_attribute << [name, type] + end + init_lines << "@__soap_attribute = {}\n" end - c.def_method("initialize", *params) do + c.def_classvar('schema_attribute', + '{' + + schema_attribute.collect { |name, type| + name.dump + ' => ' + ndq(type) + }.join(', ') + + '}' + ) + c.def_classvar('schema_element', + '{' + + schema_element.collect { |name, type| + name.dump + ' => ' + ndq(type) + }.join(', ') + + '}' + ) + c.def_method('initialize', *params) do init_lines end c.dump end + def basetype_class(type) + if @simpletypes[type] + basetype_mapped_class(@simpletypes[type].base) + else + basetype_mapped_class(type) + end + end + def dump_arraydef(complextype) qname = complextype.name - c = XSD::CodeGen::ClassDef.new(create_class_name(qname), "::Array") + c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::Array') c.comment = "#{ qname.namespace }" - c.def_classvar("schema_type", qname.name.dump) - c.def_classvar("schema_ns", qname.namespace.dump) + c.def_classvar('schema_type', qname.name.dump) + c.def_classvar('schema_ns', qname.namespace.dump) c.dump end end diff --git a/lib/wsdl/soap/classDefCreatorSupport.rb b/lib/wsdl/soap/classDefCreatorSupport.rb index dbcc55f7b9..706c00d4f6 100644 --- a/lib/wsdl/soap/classDefCreatorSupport.rb +++ b/lib/wsdl/soap/classDefCreatorSupport.rb @@ -59,6 +59,18 @@ __EOD__ str end + def dq(ele) + ele.dump + end + + def ndq(ele) + ele.nil? ? 'nil' : dq(ele) + end + + def sym(ele) + ':' + ele + end + private def dump_inout_type(param) @@ -66,10 +78,14 @@ private message = param.find_message params = "" message.parts.each do |part| - next unless part.type name = safevarname(part.name) - typename = safeconstname(part.type.name) - params << add_at("# #{name}", "#{typename} - #{part.type}\n", 20) + if part.type + typename = safeconstname(part.type.name) + params << add_at("# #{name}", "#{typename} - #{part.type}\n", 20) + elsif part.element + typename = safeconstname(part.element.name) + params << add_at("# #{name}", "#{typename} - #{part.element}\n", 20) + end end unless params.empty? return params diff --git a/lib/wsdl/soap/clientSkeltonCreator.rb b/lib/wsdl/soap/clientSkeltonCreator.rb index 9c538dd612..0b9e79c718 100644 --- a/lib/wsdl/soap/clientSkeltonCreator.rb +++ b/lib/wsdl/soap/clientSkeltonCreator.rb @@ -62,7 +62,7 @@ __EOD__ def dump_input_init(input) result = input.find_message.parts.collect { |part| - "#{ uncapitalize(part.name) }" + safevarname(part.name) }.join(" = ") if result.empty? "" diff --git a/lib/wsdl/soap/complexType.rb b/lib/wsdl/soap/complexType.rb index 34fc18f1a4..1bed059f7e 100644 --- a/lib/wsdl/soap/complexType.rb +++ b/lib/wsdl/soap/complexType.rb @@ -7,6 +7,7 @@ require 'wsdl/xmlSchema/complexType' +require 'soap/mapping' module WSDL @@ -20,37 +21,58 @@ class ComplexType < Info def check_type if content - if content.elements.size == 1 and content.elements[0].maxoccurs != 1 - :TYPE_ARRAY + if attributes.empty? and + content.elements.size == 1 and content.elements[0].maxoccurs != '1' + if name == ::SOAP::Mapping::MapQName + :TYPE_MAP + else + :TYPE_ARRAY + end else :TYPE_STRUCT end elsif complexcontent and complexcontent.base == ::SOAP::ValueArrayName :TYPE_ARRAY + elsif simplecontent + :TYPE_SIMPLE + elsif !attributes.empty? + :TYPE_STRUCT else raise NotImplementedError.new("Unknown kind of complexType.") end end def child_type(name = nil) + type = nil case compoundtype when :TYPE_STRUCT if ele = find_element(name) - ele.type + type = ele.type elsif ele = find_element_by_name(name.name) - ele.type - else - nil + type = ele.type end when :TYPE_ARRAY - @contenttype ||= content_arytype + type = @contenttype ||= content_arytype + when :TYPE_MAP + item_ele = find_element_by_name("item") or + raise RuntimeError.new("'item' element not found in Map definition.") + content = item_ele.local_complextype or + raise RuntimeError.new("No complexType definition for 'item'.") + if ele = content.find_element(name) + type = ele.type + elsif ele = content.find_element_by_name(name.name) + type = ele.type + end + else + raise NotImplementedError.new("Unknown kind of complexType.") end + type end def child_defined_complextype(name) ele = nil case compoundtype - when :TYPE_STRUCT + when :TYPE_STRUCT, :TYPE_MAP unless ele = find_element(name) if name.namespace.nil? ele = find_element_by_name(name.name) @@ -81,7 +103,7 @@ class ComplexType < Info return attribute.arytype end end - elsif content.elements.size == 1 and content.elements[0].maxoccurs != 1 + elsif content.elements.size == 1 and content.elements[0].maxoccurs != '1' return content.elements[0].type else raise RuntimeError.new("Assert: Unknown array definition.") diff --git a/lib/wsdl/soap/data.rb b/lib/wsdl/soap/data.rb index 23aaff83b5..48512d3751 100644 --- a/lib/wsdl/soap/data.rb +++ b/lib/wsdl/soap/data.rb @@ -11,6 +11,7 @@ require 'wsdl/soap/definitions' require 'wsdl/soap/binding' require 'wsdl/soap/operation' require 'wsdl/soap/body' +require 'wsdl/soap/element' require 'wsdl/soap/header' require 'wsdl/soap/headerfault' require 'wsdl/soap/fault' diff --git a/lib/wsdl/soap/driverCreator.rb b/lib/wsdl/soap/driverCreator.rb index 50be8ed1dc..b752ee336d 100644 --- a/lib/wsdl/soap/driverCreator.rb +++ b/lib/wsdl/soap/driverCreator.rb @@ -68,10 +68,15 @@ Methods = [ end c.def_privatemethod("init_methods") do <<-EOD - Methods.each do |name_as, name, params, soapaction, namespace| + Methods.each do |name_as, name, params, soapaction, namespace, style| qname = ::XSD::QName.new(namespace, name_as) - @proxy.add_method(qname, soapaction, name, params) - add_rpc_method_interface(name, params) + if style == :document + @proxy.add_document_method(qname, soapaction, name, params) + add_document_method_interface(name, name_as) + else + @proxy.add_rpc_method(qname, soapaction, name, params) + add_rpc_method_interface(name, params) + end end EOD end diff --git a/lib/wsdl/soap/element.rb b/lib/wsdl/soap/element.rb new file mode 100644 index 0000000000..c39a00d25a --- /dev/null +++ b/lib/wsdl/soap/element.rb @@ -0,0 +1,34 @@ +# WSDL4R - XMLSchema element definition for WSDL. +# Copyright (C) 2004 NAKAMURA, Hiroshi <[email protected]>. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/xmlSchema/element' + + +module WSDL +module XMLSchema + + +class Element < Info + def map_as_array? + maxoccurs != '1' + end + + def attributes + @local_complextype.attributes + end + + def each_element + @local_complextype.each_element do |element| + yield(element) + end + end +end + + +end +end diff --git a/lib/wsdl/soap/fault.rb b/lib/wsdl/soap/fault.rb index abd3cbe3dd..019c881f97 100644 --- a/lib/wsdl/soap/fault.rb +++ b/lib/wsdl/soap/fault.rb @@ -34,13 +34,13 @@ class Fault < Info def parse_attr(attr, value) case attr when NameAttrName - @name = value + @name = XSD::QName.new(targetnamespace, value.source) when UseAttrName - @use = value + @use = value.source when EncodingStyleAttrName - @encodingstyle = value + @encodingstyle = value.source when NamespaceAttrName - @namespace = value + @namespace = value.source else nil end diff --git a/lib/wsdl/soap/header.rb b/lib/wsdl/soap/header.rb index f1dd69eafb..247531a76d 100644 --- a/lib/wsdl/soap/header.rb +++ b/lib/wsdl/soap/header.rb @@ -59,15 +59,15 @@ class Header < Info def parse_attr(attr, value) case attr when MessageAttrName - @message = value + @message = XSD::QName.new(targetnamespace, value.source) when PartAttrName - @part = value + @part = value.source when UseAttrName - @use = value + @use = value.source when EncodingStyleAttrName - @encodingstyle = value + @encodingstyle = value.source when NamespaceAttrName - @namespace = value + @namespace = value.source else nil end diff --git a/lib/wsdl/soap/headerfault.rb b/lib/wsdl/soap/headerfault.rb index a6e86661c2..d6b14f2646 100644 --- a/lib/wsdl/soap/headerfault.rb +++ b/lib/wsdl/soap/headerfault.rb @@ -38,13 +38,13 @@ class HeaderFault < Info when MessageAttrName @message = value when PartAttrName - @part = value + @part = value.source when UseAttrName - @use = value + @use = value.source when EncodingStyleAttrName - @encodingstyle = value + @encodingstyle = value.source when NamespaceAttrName - @namespace = value + @namespace = value.source else nil end diff --git a/lib/wsdl/soap/methodDefCreator.rb b/lib/wsdl/soap/methodDefCreator.rb index eded972cdc..59b8ee4253 100644 --- a/lib/wsdl/soap/methodDefCreator.rb +++ b/lib/wsdl/soap/methodDefCreator.rb @@ -45,9 +45,15 @@ private def dump_method(operation, binding) name = safemethodname(operation.name.name) name_as = operation.name.name - params = collect_parameter(operation) - soapaction = binding.soapoperation.soapaction - namespace = binding.input.soapbody.namespace + stylestr = binding.soapoperation.operation_style.id2name + if binding.soapoperation.operation_style == :rpc + soapaction = binding.soapoperation.soapaction + namespace = binding.input.soapbody.namespace + params = collect_rpcparameter(operation) + else + soapaction = namespace = nil + params = collect_documentparameter(operation) + end paramstr = param2str(params) if paramstr.empty? paramstr = '[]' @@ -57,36 +63,45 @@ private return <<__EOD__ [#{ dq(name_as) }, #{ dq(name) }, #{ paramstr }, - #{ soapaction ? dq(soapaction) : "nil" }, #{ dq(namespace) } + #{ ndq(soapaction) }, #{ ndq(namespace) }, #{ sym(stylestr) } ] __EOD__ end - def collect_parameter(operation) + def collect_rpcparameter(operation) result = operation.inputparts.collect { |part| collect_type(part.type) - param_set('in', definedtype(part), part.name) + param_set('in', rpcdefinedtype(part), part.name) } outparts = operation.outputparts if outparts.size > 0 retval = outparts[0] collect_type(retval.type) - result << param_set('retval', definedtype(retval), retval.name) + result << param_set('retval', rpcdefinedtype(retval), retval.name) cdr(outparts).each { |part| collect_type(part.type) - result << param_set('out', definedtype(part), part.name) + result << param_set('out', rpcdefinedtype(part), part.name) } end result end - def definedtype(part) + def collect_documentparameter(operation) + input = operation.inputparts[0] + output = operation.outputparts[0] + [ + param_set('input', documentdefinedtype(input), input.name), + param_set('output', documentdefinedtype(output), output.name) + ] + end + + def rpcdefinedtype(part) if mapped = basetype_mapped_class(part.type) ['::' + mapped.name] - elsif definedelement = @elements[part.element] - raise RuntimeError.new("Part: #{part.name} should be typed for RPC service for now.") elsif definedtype = @simpletypes[part.type] ['::' + basetype_mapped_class(definedtype.base).name] + elsif definedtype = @elements[part.element] + ['::SOAP::SOAPStruct', part.element.namespace, part.element.name] elsif definedtype = @complextypes[part.type] case definedtype.compoundtype when :TYPE_STRUCT @@ -104,6 +119,18 @@ __EOD__ end end + def documentdefinedtype(part) + if definedtype = @simpletypes[part.type] + ['::' + basetype_mapped_class(definedtype.base).name, nil, part.name] + elsif definedtype = @elements[part.element] + ['::SOAP::SOAPElement', part.element.namespace, part.element.name] + elsif definedtype = @complextypes[part.type] + ['::SOAP::SOAPElement', part.type.namespace, part.type.name] + else + raise RuntimeError.new("Part: #{part.name} cannot be resolved.") + end + end + def param_set(io_type, type, name) [io_type, type, name] end @@ -128,14 +155,10 @@ __EOD__ if type.size == 1 "[#{ type[0] }]" else - "[#{ type[0] }, #{ dq(type[1]) }, #{ dq(type[2]) }]" + "[#{ type[0] }, #{ ndq(type[1]) }, #{ dq(type[2]) }]" end end - def dq(ele) - "\"" << ele << "\"" - end - def cdr(ary) result = ary.dup result.shift diff --git a/lib/wsdl/soap/operation.rb b/lib/wsdl/soap/operation.rb index bb49f2099c..51bb2e9403 100644 --- a/lib/wsdl/soap/operation.rb +++ b/lib/wsdl/soap/operation.rb @@ -50,13 +50,14 @@ class Operation < Info def parse_attr(attr, value) case attr when StyleAttrName - if ["document", "rpc"].include?(value) - @style = value.intern + if ["document", "rpc"].include?(value.source) + @style = value.source.intern else - raise AttributeConstraintError.new("Unexpected value #{ value }.") + raise Parser::AttributeConstraintError.new( + "Unexpected value #{ value }.") end when SOAPActionAttrName - @soapaction = value + @soapaction = value.source else nil end diff --git a/lib/wsdl/soap/servantSkeltonCreator.rb b/lib/wsdl/soap/servantSkeltonCreator.rb index bf293949b8..12761ab5b4 100644 --- a/lib/wsdl/soap/servantSkeltonCreator.rb +++ b/lib/wsdl/soap/servantSkeltonCreator.rb @@ -45,14 +45,16 @@ private c = ::XSD::CodeGen::ClassDef.new(class_name) operations = @definitions.porttype(name).operations operations.each do |operation| - name = operation.name.name + name = safemethodname(operation.name.name) input = operation.input - m = ::XSD::CodeGen::MethodDef.new(name, - input.find_message.parts.collect { |part| safevarname(part.name) }) do - <<-EOD - raise NotImplementedError.new - EOD - end + params = input.find_message.parts.collect { |part| + safevarname(part.name) + } + m = ::XSD::CodeGen::MethodDef.new(name, params) do <<-EOD + p [#{params.join(", ")}] + raise NotImplementedError.new + EOD + end m.comment = dump_method_signature(operation) c.add_method(m) end diff --git a/lib/wsdl/soap/standaloneServerStubCreator.rb b/lib/wsdl/soap/standaloneServerStubCreator.rb index 34bcfdbba9..779139a5f4 100644 --- a/lib/wsdl/soap/standaloneServerStubCreator.rb +++ b/lib/wsdl/soap/standaloneServerStubCreator.rb @@ -57,9 +57,13 @@ Methods = [ <<-EOD super(*arg) servant = #{class_name}.new - #{class_name}::Methods.each do |name_as, name, params, soapaction, ns| - qname = XSD::QName.new(ns, name_as) - @soaplet.app_scope_router.add_method(servant, qname, soapaction, name, params) + #{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style| + qname = XSD::QName.new(namespace, name_as) + if style == :document + @soaplet.app_scope_router.add_document_method(servant, qname, soapaction, name, param_def) + else + @soaplet.app_scope_router.add_rpc_method(servant, qname, soapaction, name, param_def) + end end self.mapping_registry = #{class_name}::MappingRegistry EOD @@ -68,7 +72,11 @@ Methods = [ if $0 == __FILE__ # Change listen port. - #{class_name}App.new('app', nil, '0.0.0.0', 10080).start + server = #{class_name}App.new('app', nil, '0.0.0.0', 10080) + trap(:INT) do + server.shutdown + end + server.start end EOD end diff --git a/lib/wsdl/xmlSchema/all.rb b/lib/wsdl/xmlSchema/all.rb index 53f7ae82e4..1cb1ac16ea 100644 --- a/lib/wsdl/xmlSchema/all.rb +++ b/lib/wsdl/xmlSchema/all.rb @@ -20,8 +20,8 @@ class All < Info def initialize super() - @minoccurs = 1 - @maxoccurs = 1 + @minoccurs = '1' + @maxoccurs = '1' @elements = [] end @@ -51,9 +51,9 @@ class All < Info def parse_attr(attr, value) case attr when MaxOccursAttrName - @maxoccurs = value + @maxoccurs = value.source when MinOccursAttrName - @minoccurs = value + @minoccurs = value.source else nil end diff --git a/lib/wsdl/xmlSchema/any.rb b/lib/wsdl/xmlSchema/any.rb index 3fc3706182..72d25e8dde 100644 --- a/lib/wsdl/xmlSchema/any.rb +++ b/lib/wsdl/xmlSchema/any.rb @@ -21,8 +21,8 @@ class Any < Info def initialize super() - @maxoccurs = 1 - @minoccurs = 1 + @maxoccurs = '1' + @minoccurs = '1' @namespace = '##any' @process_contents = 'strict' end @@ -38,13 +38,13 @@ class Any < Info def parse_attr(attr, value) case attr when MaxOccursAttrName - @maxoccurs = value + @maxoccurs = value.source when MinOccursAttrName - @minoccurs = value + @minoccurs = value.source when NamespaceAttrName - @namespace = value + @namespace = value.source when ProcessContentsAttrName - @process_contents = value + @process_contents = value.source else nil end diff --git a/lib/wsdl/xmlSchema/attribute.rb b/lib/wsdl/xmlSchema/attribute.rb index e5046dd991..6861fc171e 100644 --- a/lib/wsdl/xmlSchema/attribute.rb +++ b/lib/wsdl/xmlSchema/attribute.rb @@ -33,10 +33,13 @@ class Attribute < Info @type = nil @default = nil @fixed = nil - @arytype = nil end + def targetnamespace + parent.targetnamespace + end + def parse_element(element) nil end @@ -46,23 +49,23 @@ class Attribute < Info when RefAttrName @ref = value when UseAttrName - @use = value + @use = value.source when FormAttrName - @form = value + @form = value.source when NameAttrName - @name = value + @name = XSD::QName.new(targetnamespace, value.source) when TypeAttrName @type = value when DefaultAttrName - @default = value + @default = value.source when FixedAttrName - @fixed = value + @fixed = value.source when ArrayTypeAttrName - @arytype = if value.is_a?(XSD::QName) - value - else - XSD::QName.new(XSD::Namespace, value) - end + @arytype = if value.namespace.nil? + XSD::QName.new(XSD::Namespace, value.source) + else + value + end else nil end diff --git a/lib/wsdl/xmlSchema/choice.rb b/lib/wsdl/xmlSchema/choice.rb index 4cf481ec9e..435fd48e49 100644 --- a/lib/wsdl/xmlSchema/choice.rb +++ b/lib/wsdl/xmlSchema/choice.rb @@ -20,8 +20,8 @@ class Choice < Info def initialize super() - @minoccurs = 1 - @maxoccurs = 1 + @minoccurs = '1' + @maxoccurs = '1' @elements = [] end @@ -51,9 +51,9 @@ class Choice < Info def parse_attr(attr, value) case attr when MaxOccursAttrName - @maxoccurs = value + @maxoccurs = value.source when MinOccursAttrName - @minoccurs = value + @minoccurs = value.source else nil end diff --git a/lib/wsdl/xmlSchema/complexType.rb b/lib/wsdl/xmlSchema/complexType.rb index 056a806dc5..0d9c622c74 100644 --- a/lib/wsdl/xmlSchema/complexType.rb +++ b/lib/wsdl/xmlSchema/complexType.rb @@ -19,7 +19,8 @@ module XMLSchema class ComplexType < Info attr_accessor :name attr_accessor :complexcontent - attr_accessor :content + attr_accessor :simplecontent + attr_reader :content attr_accessor :final attr_accessor :mixed attr_reader :attributes @@ -28,6 +29,7 @@ class ComplexType < Info super() @name = name @complexcontent = nil + @simplecontent = nil @content = nil @final = nil @mixed = false @@ -35,13 +37,13 @@ class ComplexType < Info end def targetnamespace - parent.targetnamespace + parent.is_a?(WSDL::XMLSchema::Element) ? nil : parent.targetnamespace end AnyAsElement = Element.new(XSD::QName.new(nil, 'any'), XSD::AnyTypeName) def each_element - if @content - @content.elements.each do |element| + if content + content.elements.each do |element| if element.is_a?(Any) yield(AnyAsElement) else @@ -52,8 +54,8 @@ class ComplexType < Info end def find_element(name) - if @content - @content.elements.each do |element| + if content + content.elements.each do |element| if element.is_a?(Any) return AnyAsElement if name == AnyAsElement.name else @@ -65,8 +67,8 @@ class ComplexType < Info end def find_element_by_name(name) - if @content - @content.elements.each do |element| + if content + content.elements.each do |element| if element.is_a?(Any) return AnyAsElement if name == AnyAsElement.name.name else @@ -95,16 +97,14 @@ class ComplexType < Info case element when AllName @content = All.new - @content when SequenceName @content = Sequence.new - @content when ChoiceName @content = Choice.new - @content when ComplexContentName @complexcontent = ComplexContent.new - @complexcontent + when SimpleContentName + @simplecontent = SimpleContent.new when AttributeName o = Attribute.new @attributes << o @@ -117,11 +117,11 @@ class ComplexType < Info def parse_attr(attr, value) case attr when FinalAttrName - @final = value + @final = value.source when MixedAttrName - @mixed = (value == 'true') + @mixed = (value.source == 'true') when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) else nil end diff --git a/lib/wsdl/xmlSchema/content.rb b/lib/wsdl/xmlSchema/content.rb index 3aa875e3e7..2f1dfb4b6c 100644 --- a/lib/wsdl/xmlSchema/content.rb +++ b/lib/wsdl/xmlSchema/content.rb @@ -67,9 +67,9 @@ class Content < Info def parse_attr(attr, value) case attr when FinalAttrName - @final = value + @final = value.source when MixedAttrName - @mixed = (value == 'true') + @mixed = (value.source == 'true') else nil end diff --git a/lib/wsdl/xmlSchema/data.rb b/lib/wsdl/xmlSchema/data.rb index 1283ac2a1d..10bc343adb 100644 --- a/lib/wsdl/xmlSchema/data.rb +++ b/lib/wsdl/xmlSchema/data.rb @@ -13,6 +13,7 @@ require 'wsdl/xmlSchema/simpleType' require 'wsdl/xmlSchema/simpleRestriction' require 'wsdl/xmlSchema/complexType' require 'wsdl/xmlSchema/complexContent' +require 'wsdl/xmlSchema/simpleContent' require 'wsdl/xmlSchema/any' require 'wsdl/xmlSchema/element' require 'wsdl/xmlSchema/all' @@ -39,6 +40,7 @@ ImportName = XSD::QName.new(XSD::Namespace, 'import') RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction') SequenceName = XSD::QName.new(XSD::Namespace, 'sequence') SchemaName = XSD::QName.new(XSD::Namespace, 'schema') +SimpleContentName = XSD::QName.new(XSD::Namespace, 'simpleContent') SimpleTypeName = XSD::QName.new(XSD::Namespace, 'simpleType') UniqueName = XSD::QName.new(XSD::Namespace, 'unique') @@ -56,6 +58,7 @@ MixedAttrName = XSD::QName.new(nil, 'mixed') NameAttrName = XSD::QName.new(nil, 'name') NamespaceAttrName = XSD::QName.new(nil, 'namespace') NillableAttrName = XSD::QName.new(nil, 'nillable') +ProcessContentsAttrName = XSD::QName.new(nil, 'processContents') RefAttrName = XSD::QName.new(nil, 'ref') SchemaLocationAttrName = XSD::QName.new(nil, 'schemaLocation') TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace') diff --git a/lib/wsdl/xmlSchema/element.rb b/lib/wsdl/xmlSchema/element.rb index 90e8c0d5d1..cc9d4e9ed8 100644 --- a/lib/wsdl/xmlSchema/element.rb +++ b/lib/wsdl/xmlSchema/element.rb @@ -28,8 +28,8 @@ class Element < Info @type = type @local_complextype = nil @constraint = nil - @maxoccurs = 1 - @minoccurs = 1 + @maxoccurs = '1' + @minoccurs = '1' @nillable = nil end @@ -37,6 +37,11 @@ class Element < Info parent.targetnamespace end + def elementform + # ToDo: must be overwritten. + parent.elementformdefault + end + def parse_element(element) case element when ComplexTypeName @@ -54,45 +59,27 @@ class Element < Info def parse_attr(attr, value) case attr when NameAttrName - #@name = XSD::QName.new(nil, value) - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) when TypeAttrName - @type = if value.is_a?(XSD::QName) - value - else - XSD::QName.new(XSD::Namespace, value) - end + @type = value when MaxOccursAttrName - case parent - when All - if value != '1' + if parent.is_a?(All) + if value.source != '1' raise Parser::AttrConstraintError.new( "Cannot parse #{ value } for #{ attr }.") end - @maxoccurs = value - when Sequence - @maxoccurs = value - else - raise NotImplementedError.new end - @maxoccurs + @maxoccurs = value.source when MinOccursAttrName - case parent - when All - if ['0', '1'].include?(value) - @minoccurs = value - else + if parent.is_a?(All) + unless ['0', '1'].include?(value.source) raise Parser::AttrConstraintError.new( "Cannot parse #{ value } for #{ attr }.") end - when Sequence - @minoccurs = value - else - raise NotImplementedError.new end - @minoccurs + @minoccurs = value.source when NillableAttrName - @nillable = (value == 'true') + @nillable = (value.source == 'true') else nil end diff --git a/lib/wsdl/xmlSchema/enumeration.rb b/lib/wsdl/xmlSchema/enumeration.rb index cd61572d07..5a16476032 100644 --- a/lib/wsdl/xmlSchema/enumeration.rb +++ b/lib/wsdl/xmlSchema/enumeration.rb @@ -25,8 +25,8 @@ class Enumeration < Info def parse_attr(attr, value) case attr when ValueAttrName - parent.enumeration << value - value + parent.enumeration << value.source + value.source end end end diff --git a/lib/wsdl/xmlSchema/import.rb b/lib/wsdl/xmlSchema/import.rb index 2ef3b72ab2..e65641330d 100644 --- a/lib/wsdl/xmlSchema/import.rb +++ b/lib/wsdl/xmlSchema/import.rb @@ -30,9 +30,9 @@ class Import < Info def parse_attr(attr, value) case attr when NamespaceAttrName - @namespace = value + @namespace = value.source when SchemaLocationAttrName - @schemalocation = value + @schemalocation = value.source else nil end diff --git a/lib/wsdl/xmlSchema/parser.rb b/lib/wsdl/xmlSchema/parser.rb index 5401c5f729..a7f1c29fd4 100644 --- a/lib/wsdl/xmlSchema/parser.rb +++ b/lib/wsdl/xmlSchema/parser.rb @@ -22,7 +22,7 @@ class Parser include XSD class ParseError < Error; end - class FormatDecodeError < Error; end + class FormatDecodeError < ParseError; end class UnknownElementError < FormatDecodeError; end class UnknownAttributeError < FormatDecodeError; end class UnexpectedElementError < FormatDecodeError; end @@ -114,34 +114,21 @@ private else o = parent.parse_element(element) unless o - raise UnknownElementError.new("Unknown element #{ element }.") + STDERR.puts("Unknown element #{ element }.") + o = Documentation.new # which accepts any element. end # node could be a pseudo element. pseudo element has its own parent. o.parent = parent if o.parent.nil? end attrs.each do |key, value| - attr = unless /:/ =~ key - XSD::QName.new(nil, key) - else - ns.parse(key) - end - value_ele = if /:/ !~ value - value - elsif /^http:\/\// =~ value # ToDo: ugly. - value - else - begin - ns.parse(value) - rescue - value - end - end - if attr == IdAttrName + attr_ele = ns.parse(key, true) + value_ele = ns.parse(value, true) + value_ele.source = value # for recovery; value may not be a QName + if attr_ele == IdAttrName o.id = value_ele else - unless o.parse_attr(attr, value_ele) - STDERR.puts("Unknown attr #{ attr }.") - # raise UnknownAttributeError.new("Unknown attr #{ attr }.") + unless o.parse_attr(attr_ele, value_ele) + STDERR.puts("Unknown attr #{ attr_ele }.") end end end diff --git a/lib/wsdl/xmlSchema/schema.rb b/lib/wsdl/xmlSchema/schema.rb index b530a92556..ddd231bd97 100644 --- a/lib/wsdl/xmlSchema/schema.rb +++ b/lib/wsdl/xmlSchema/schema.rb @@ -32,7 +32,7 @@ class Schema < Info @elements = XSD::NamedElements.new @attributes = XSD::NamedElements.new @imports = [] - @elementformdefault = nil + @elementformdefault = "qualified" end def parse_element(element) @@ -64,11 +64,11 @@ class Schema < Info def parse_attr(attr, value) case attr when TargetNamespaceAttrName - @targetnamespace = value + @targetnamespace = value.source when AttributeFormDefaultAttrName - @attributeformdefault = value + @attributeformdefault = value.source when ElementFormDefaultAttrName - @elementformdefault = value + @elementformdefault = value.source else nil end diff --git a/lib/wsdl/xmlSchema/sequence.rb b/lib/wsdl/xmlSchema/sequence.rb index 3810832ab2..bffb6a009d 100644 --- a/lib/wsdl/xmlSchema/sequence.rb +++ b/lib/wsdl/xmlSchema/sequence.rb @@ -20,8 +20,8 @@ class Sequence < Info def initialize super() - @minoccurs = 1 - @maxoccurs = 1 + @minoccurs = '1' + @maxoccurs = '1' @elements = [] end @@ -51,9 +51,9 @@ class Sequence < Info def parse_attr(attr, value) case attr when MaxOccursAttrName - @maxoccurs = value + @maxoccurs = value.source when MinOccursAttrName - @minoccurs = value + @minoccurs = value.source else nil end diff --git a/lib/wsdl/xmlSchema/simpleContent.rb b/lib/wsdl/xmlSchema/simpleContent.rb new file mode 100644 index 0000000000..0d83678a01 --- /dev/null +++ b/lib/wsdl/xmlSchema/simpleContent.rb @@ -0,0 +1,65 @@ +# WSDL4R - XMLSchema simpleContent definition for WSDL. +# Copyright (C) 2004 NAKAMURA, Hiroshi <[email protected]>. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'wsdl/info' +require 'xsd/namedelements' + + +module WSDL +module XMLSchema + + +class SimpleContent < Info + attr_accessor :base + attr_reader :derivetype + attr_reader :content + attr_reader :attributes + + def initialize + super + @base = nil + @derivetype = nil + @content = nil + @attributes = XSD::NamedElements.new + end + + def targetnamespace + parent.targetnamespace + end + + def parse_element(element) + case element + when RestrictionName, ExtensionName + @derivetype = element.name + self + when AttributeName + if @derivetype.nil? + raise Parser::ElementConstraintError.new("base attr not found.") + end + o = Attribute.new + @attributes << o + o + end + end + + def parse_attr(attr, value) + if @derivetype.nil? + return nil + end + case attr + when BaseAttrName + @base = value + else + nil + end + end +end + + +end +end diff --git a/lib/wsdl/xmlSchema/simpleType.rb b/lib/wsdl/xmlSchema/simpleType.rb index 830086f99e..d9f76f345c 100644 --- a/lib/wsdl/xmlSchema/simpleType.rb +++ b/lib/wsdl/xmlSchema/simpleType.rb @@ -63,7 +63,7 @@ class SimpleType < Info def parse_attr(attr, value) case attr when NameAttrName - @name = XSD::QName.new(targetnamespace, value) + @name = XSD::QName.new(targetnamespace, value.source) end end diff --git a/lib/xsd/charset.rb b/lib/xsd/charset.rb index e0241fdebc..acdea8bcf3 100644 --- a/lib/xsd/charset.rb +++ b/lib/xsd/charset.rb @@ -103,7 +103,11 @@ public end def Charset.charset_str(label) - CharsetMap.key(label.downcase) + if CharsetMap.respond_to?(:key) + CharsetMap.key(label.downcase) + else + CharsetMap.index(label.downcase) + end end # us_ascii = '[\x00-\x7F]' diff --git a/lib/xsd/codegen/methoddef.rb b/lib/xsd/codegen/methoddef.rb index 797a4f024e..24a9168d58 100644 --- a/lib/xsd/codegen/methoddef.rb +++ b/lib/xsd/codegen/methoddef.rb @@ -22,7 +22,7 @@ class MethodDef def initialize(name, *params) unless safemethodname?(name) - raise ArgumentError.new("#{name} seems to be unsafe") + raise ArgumentError.new("name '#{name}' seems to be unsafe") end @name = name @params = params diff --git a/lib/xsd/datatypes.rb b/lib/xsd/datatypes.rb index 7173f52d65..5f77ea2e4a 100644 --- a/lib/xsd/datatypes.rb +++ b/lib/xsd/datatypes.rb @@ -46,10 +46,30 @@ AnyURILiteral = 'anyURI' QNameLiteral = 'QName' NormalizedStringLiteral = 'normalizedString' +#3.3.2 token +#3.3.3 language +#3.3.4 NMTOKEN +#3.3.5 NMTOKENS +#3.3.6 Name +#3.3.7 NCName +#3.3.8 ID +#3.3.9 IDREF +#3.3.10 IDREFS +#3.3.11 ENTITY +#3.3.12 ENTITIES IntegerLiteral = 'integer' +NonPositiveIntegerLiteral = 'nonPositiveInteger' +NegativeIntegerLiteral = 'negativeInteger' LongLiteral = 'long' IntLiteral = 'int' ShortLiteral = 'short' +ByteLiteral = 'byte' +NonNegativeIntegerLiteral = 'nonNegativeInteger' +UnsignedLongLiteral = 'unsignedLong' +UnsignedIntLiteral = 'unsignedInt' +UnsignedShortLiteral = 'unsignedShort' +UnsignedByteLiteral = 'unsignedByte' +PositiveIntegerLiteral = 'positiveInteger' AttrTypeName = QName.new(InstanceNamespace, AttrType) AttrNilName = QName.new(InstanceNamespace, NilLiteral) @@ -78,7 +98,10 @@ class NSDBase end def initialize - @type = nil + end + + def init(type) + @type = type end end @@ -96,11 +119,7 @@ class XSDAnySimpleType < NSDBase attr_accessor :is_nil def initialize(value = nil) - super() - @type = Type - @data = nil - @is_nil = true - set(value) if value + init(Type, value) end # true or raise @@ -115,6 +134,7 @@ class XSDAnySimpleType < NSDBase if value.nil? @is_nil = true @data = nil + _set(nil) else @is_nil = false _set(screen_data(value)) @@ -132,6 +152,11 @@ class XSDAnySimpleType < NSDBase private + def init(type, value) + super(type) + set(value) + end + # raises ValueSpaceError if check failed def screen_data(value) value @@ -151,9 +176,7 @@ class XSDNil < XSDAnySimpleType Value = 'true' def initialize(value = nil) - super() - @type = Type - set(value) + init(Type, value) end end @@ -165,9 +188,7 @@ class XSDString < XSDAnySimpleType Type = QName.new(Namespace, StringLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -184,9 +205,7 @@ class XSDBoolean < XSDAnySimpleType Type = QName.new(Namespace, BooleanLiteral) def initialize(value = nil) - super() - @type = Type - set(value) + init(Type, value) end private @@ -211,12 +230,7 @@ class XSDDecimal < XSDAnySimpleType Type = QName.new(Namespace, DecimalLiteral) def initialize(value = nil) - super() - @type = Type - @sign = '' - @number = '' - @point = 0 - set(value) if value + init(Type, value) end def nonzero? @@ -256,8 +270,12 @@ private [sign, point, number] end - def _set(pair) - @sign, @point, @number = pair + def _set(data) + if data.nil? + @sign = @point = @number = @data = nil + return + end + @sign, @point, @number = data @data = _to_s @data.freeze end @@ -286,9 +304,7 @@ class XSDFloat < XSDAnySimpleType Type = QName.new(Namespace, FloatLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -354,9 +370,7 @@ class XSDDouble < XSDAnySimpleType Type = QName.new(Namespace, DoubleLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -417,16 +431,7 @@ class XSDDuration < XSDAnySimpleType attr_accessor :sec def initialize(value = nil) - super() - @type = Type - @sign = nil - @year = nil - @month = nil - @day = nil - @hour = nil - @min = nil - @sec = nil - set(value) if value + init(Type, value) end private @@ -450,8 +455,12 @@ private [sign, year, month, day, hour, min, sec] end - def _set(ary) - @sign, @year, @month, @day, @hour, @min, @sec = ary + def _set(data) + if data.nil? + @sign = @year = @month = @day = @hour = @min = @sec = @data = nil + return + end + @sign, @year, @month, @day, @hour, @min, @sec = data @data = _to_s @data.freeze end @@ -554,10 +563,7 @@ class XSDDateTime < XSDAnySimpleType Type = QName.new(Namespace, DateTimeLiteral) def initialize(value = nil) - super() - @type = Type - @secfrac = nil - set(value) if value + init(Type, value) end private @@ -591,8 +597,12 @@ private [data, secfrac] end - def _set(pair) - @data, @secfrac = pair + def _set(data) + if data.nil? + @data = @secfrac = nil + return + end + @data, @secfrac = data end def _to_s @@ -616,10 +626,7 @@ class XSDTime < XSDAnySimpleType Type = QName.new(Namespace, TimeLiteral) def initialize(value = nil) - super() - @type = Type - @secfrac = nil - set(value) if value + init(Type, value) end private @@ -642,8 +649,12 @@ private [data, secfrac] end - def _set(pair) - @data, @secfrac = pair + def _set(data) + if data.nil? + @data = @secfrac = nil + return + end + @data, @secfrac = data end def _to_s @@ -665,9 +676,7 @@ class XSDDate < XSDAnySimpleType Type = QName.new(Namespace, DateLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -699,9 +708,7 @@ class XSDGYearMonth < XSDAnySimpleType Type = QName.new(Namespace, GYearMonthLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -732,9 +739,7 @@ class XSDGYear < XSDAnySimpleType Type = QName.new(Namespace, GYearLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -764,9 +769,7 @@ class XSDGMonthDay < XSDAnySimpleType Type = QName.new(Namespace, GMonthDayLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -793,9 +796,7 @@ class XSDGDay < XSDAnySimpleType Type = QName.new(Namespace, GDayLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -821,9 +822,7 @@ class XSDGMonth < XSDAnySimpleType Type = QName.new(Namespace, GMonthLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -849,9 +848,7 @@ class XSDHexBinary < XSDAnySimpleType # String in Ruby could be a binary. def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end def set_encoded(value) @@ -878,9 +875,7 @@ class XSDBase64Binary < XSDAnySimpleType # String in Ruby could be a binary. def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end def set_encoded(value) @@ -906,9 +901,7 @@ class XSDAnyURI < XSDAnySimpleType Type = QName.new(Namespace, AnyURILiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -926,9 +919,7 @@ class XSDQName < XSDAnySimpleType Type = QName.new(Namespace, QNameLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -943,8 +934,12 @@ private [prefix, localpart] end - def _set(pair) - @prefix, @localpart = pair + def _set(data) + if data.nil? + @prefix = @localpart = @data = nil + return + end + @prefix, @localpart = data @data = _to_s @data.freeze end @@ -966,9 +961,7 @@ class XSDNormalizedString < XSDString Type = QName.new(Namespace, NormalizedStringLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -985,9 +978,7 @@ class XSDInteger < XSDDecimal Type = QName.new(Namespace, IntegerLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private @@ -998,6 +989,9 @@ private rescue ArgumentError raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") end + unless validate(data) + raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") + end data end @@ -1008,73 +1002,96 @@ private def _to_s() @data.to_s end + + def validate(v) + max = maxinclusive + min = mininclusive + (max.nil? or v <= max) and (min.nil? or v >= min) + end + + def maxinclusive + nil + end + + def mininclusive + nil + end + + PositiveMinInclusive = 1 + def positive(v) + PositiveMinInclusive <= v + end end -class XSDLong < XSDInteger - Type = QName.new(Namespace, LongLiteral) +class XSDNonPositiveInteger < XSDInteger + Type = QName.new(Namespace, NonPositiveIntegerLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private - def screen_data_str(str) - begin - data = Integer(str) - rescue ArgumentError - raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") - end - unless validate(data) - raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") - end - data + def maxinclusive + 0 end - def _set(value) - @data = value + def mininclusive + nil end +end - MaxInclusive = +9223372036854775807 - MinInclusive = -9223372036854775808 - def validate(v) - ((MinInclusive <= v) && (v <= MaxInclusive)) +class XSDNegativeInteger < XSDNonPositiveInteger + Type = QName.new(Namespace, NegativeIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + -1 + end + + def mininclusive + nil end end -class XSDInt < XSDLong - Type = QName.new(Namespace, IntLiteral) +class XSDLong < XSDInteger + Type = QName.new(Namespace, LongLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private - def screen_data_str(str) - begin - data = Integer(str) - rescue ArgumentError - raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") - end - unless validate(data) - raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") - end - data + def maxinclusive + +9223372036854775807 end - def _set(value) - @data = value + def mininclusive + -9223372036854775808 end +end - MaxInclusive = +2147483647 - MinInclusive = -2147483648 - def validate(v) - ((MinInclusive <= v) && (v <= MaxInclusive)) +class XSDInt < XSDLong + Type = QName.new(Namespace, IntLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +2147483647 + end + + def mininclusive + -2147483648 end end @@ -1082,33 +1099,143 @@ class XSDShort < XSDInt Type = QName.new(Namespace, ShortLiteral) def initialize(value = nil) - super() - @type = Type - set(value) if value + init(Type, value) end private - def screen_data_str(str) - begin - data = Integer(str) - rescue ArgumentError - raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") - end - unless validate(data) - raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.") - end - data + def maxinclusive + +32767 end - def _set(value) - @data = value + def mininclusive + -32768 end +end - MaxInclusive = +32767 - MinInclusive = -32768 - def validate(v) - ((MinInclusive <= v) && (v <= MaxInclusive)) +class XSDByte < XSDShort + Type = QName.new(Namespace, ByteLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +127 + end + + def mininclusive + -128 + end +end + +class XSDNonNegativeInteger < XSDInteger + Type = QName.new(Namespace, NonNegativeIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + nil + end + + def mininclusive + 0 + end +end + +class XSDUnsignedLong < XSDNonNegativeInteger + Type = QName.new(Namespace, UnsignedLongLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +18446744073709551615 + end + + def mininclusive + 0 + end +end + +class XSDUnsignedInt < XSDUnsignedLong + Type = QName.new(Namespace, UnsignedIntLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +4294967295 + end + + def mininclusive + 0 + end +end + +class XSDUnsignedShort < XSDUnsignedInt + Type = QName.new(Namespace, UnsignedShortLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +65535 + end + + def mininclusive + 0 + end +end + +class XSDUnsignedByte < XSDUnsignedShort + Type = QName.new(Namespace, UnsignedByteLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + +255 + end + + def mininclusive + 0 + end +end + +class XSDPositiveInteger < XSDNonNegativeInteger + Type = QName.new(Namespace, PositiveIntegerLiteral) + + def initialize(value = nil) + init(Type, value) + end + +private + + def maxinclusive + nil + end + + def mininclusive + 1 end end diff --git a/lib/xsd/namedelements.rb b/lib/xsd/namedelements.rb index b6f909c1dc..f4d7c4f5aa 100644 --- a/lib/xsd/namedelements.rb +++ b/lib/xsd/namedelements.rb @@ -23,6 +23,10 @@ class NamedElements o end + def empty? + size == 0 + end + def size @elements.size end diff --git a/lib/xsd/ns.rb b/lib/xsd/ns.rb index 224db6c058..66bd9caf43 100644 --- a/lib/xsd/ns.rb +++ b/lib/xsd/ns.rb @@ -93,9 +93,20 @@ public # $1 and $2 are necessary. ParseRegexp = Regexp.new('^([^:]+)(?::(.+))?$') - def parse(elem) - ns = nil - name = nil + def parse(str, local = false) + if ParseRegexp =~ str + if (name = $2) and (ns = @tag2ns[$1]) + return XSD::QName.new(ns, name) + end + end + XSD::QName.new(local ? nil : @default_namespace, str) + end + + # For local attribute key parsing + # <foo xmlns="urn:" xmlns:n1="urn:" bar="1" n1:baz="2" /> + # => + # {}bar, {urn:}baz + def parse_local(elem) ParseRegexp =~ elem if $2 ns = @tag2ns[$1] @@ -104,10 +115,9 @@ public raise FormatError.new('Unknown namespace qualifier: ' << $1) end elsif $1 - ns = @default_namespace + ns = nil name = $1 - end - if !name + else raise FormatError.new("Illegal element format: #{ elem }") end XSD::QName.new(ns, name) diff --git a/lib/xsd/qname.rb b/lib/xsd/qname.rb index 7185fedf2d..ed1fa41f98 100644 --- a/lib/xsd/qname.rb +++ b/lib/xsd/qname.rb @@ -12,14 +12,16 @@ module XSD class QName attr_accessor :namespace attr_accessor :name + attr_accessor :source def initialize(namespace = nil, name = nil) @namespace = namespace @name = name + @source = nil end def dup_name(name) - ::XSD::QName.new(@namespace, name) + XSD::QName.new(@namespace, name) end def match(rhs) diff --git a/test/soap/ssl/sslsvr.rb b/test/soap/ssl/sslsvr.rb index 281c1a1a51..52a8d6878f 100644 --- a/test/soap/ssl/sslsvr.rb +++ b/test/soap/ssl/sslsvr.rb @@ -47,10 +47,18 @@ if $0 == __FILE__ :SSLClientCA => cert('ca.cert'), :SSLCertName => nil ) - trap(:INT) do - $server.shutdown if $server + t = Thread.new { + Thread.current.abort_on_exception = true + $server.start + } + while $server.status != :Running + sleep 0.1 + unless t.alive? + t.join + raise + end end STDOUT.sync = true - STDOUT.puts $$ - $server.start + puts $$ + t.join end diff --git a/test/soap/ssl/test_ssl.rb b/test/soap/ssl/test_ssl.rb index 6678c775ac..82bb890d71 100644 --- a/test/soap/ssl/test_ssl.rb +++ b/test/soap/ssl/test_ssl.rb @@ -33,12 +33,8 @@ class TestSSL < Test::Unit::TestCase teardown_server end - def streamhandler - @client.instance_eval("@servant").instance_eval("@streamhandler").client - end - def test_options - cfg = streamhandler.ssl_config + cfg = @client.streamhandler.client.ssl_config assert_nil(cfg.client_cert) assert_nil(cfg.client_key) assert_nil(cfg.client_ca) @@ -192,11 +188,14 @@ private end def teardown_server - Process.kill('INT', @serverpid) + if @serverpid + Process.kill('KILL', @serverpid) + Process.waitpid(@serverpid) + end end def teardown_client - @client.reset_stream + @client.reset_stream if @client end def verify_callback(ok, cert) diff --git a/test/soap/test_property.rb b/test/soap/test_property.rb index 5cd25a30f7..7acd2c8437 100644 --- a/test/soap/test_property.rb +++ b/test/soap/test_property.rb @@ -6,6 +6,8 @@ module SOAP class TestProperty < Test::Unit::TestCase + FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError + def setup @prop = ::SOAP::Property.new end @@ -67,7 +69,7 @@ __EOP__ prop["foo.bar"].lock prop.load("foo.bar.baz = 123") assert(hooked) - assert_raises(TypeError) do + assert_raises(FrozenError) do prop.load("foo.bar.qux = 123") end prop.load("foo.baz = 456") @@ -130,7 +132,7 @@ __EOP__ tag = Object.new tested = false @prop.add_hook("foo.bar") do |key, value| - assert_raise(RuntimeError) do + assert_raise(FrozenError) do key << "baz" end tested = true @@ -266,37 +268,37 @@ __EOP__ @prop.lock assert(@prop.locked?) assert_instance_of(::SOAP::Property, @prop["a"]) - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["b"] end # @prop["a"].lock - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a"] end assert_instance_of(::SOAP::Property, @prop["a.b"]) # @prop["a.b"].lock - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b"] end - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a"] end # @prop["a.b.c.d"].lock assert_instance_of(::SOAP::Property, @prop["a.b.c"]) - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b.c.d"] end assert_instance_of(::SOAP::Property, @prop["a.b.d"]) # branch["e"].lock assert_instance_of(::SOAP::Property, @prop["a.b.d"]) - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b.d.e"] end - assert_raises(TypeError) do + assert_raises(FrozenError) do branch["e"] end end @@ -310,26 +312,26 @@ __EOP__ assert_equal(nil, @prop["a.a"]) assert_equal(1, @prop["a.b.c"]) assert_equal(false, @prop["b"]) - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["c"] end - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["c"] = 2 end - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b.R"] end - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop.add_hook do assert(false) end end - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop.add_hook("c") do assert(false) end end - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop.add_hook("a.c") do assert(false) end @@ -364,7 +366,7 @@ __EOP__ @prop["a.b.c"] = 5 assert(tested) assert_equal(5, @prop["a.b.c"]) - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b.d"] = 5 end end @@ -383,28 +385,28 @@ __EOP__ assert_equal(branch, @prop[:a][:b][:d]) @prop.lock(true) # split error 1 - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b"] end # split error 2 - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a"] end @prop["a.b.c"] = 2 assert_equal(2, @prop["a.b.c"]) # replace error - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b.c"] = ::SOAP::Property.new end # override error - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b"] = 1 end # - assert_raises(TypeError) do + assert_raises(FrozenError) do @prop["a.b.d"] << 1 end - assert_raises(TypeError) do + assert_raises(FrozenError) do branch << 1 end branch.unlock(true) diff --git a/test/soap/test_streamhandler.rb b/test/soap/test_streamhandler.rb index e5b578d7a6..fa0080e9f1 100644 --- a/test/soap/test_streamhandler.rb +++ b/test/soap/test_streamhandler.rb @@ -143,7 +143,9 @@ __EOX__ def test_basic_auth unless Object.const_defined?('HTTPAccess2') - STDERR.puts("basic_auth is not supported under soap4r + net/http for now.") + # soap4r + net/http + basic_auth is not supported. + # use http-access2 instead. + assert(true) return end str = "" @@ -169,6 +171,10 @@ __EOX__ assert_nil(@client.do_server_proc) r, h = parse_req_header(str) assert_match(%r"POST http://localhost:17171/ HTTP/1.", r) + # illegal proxy uri + assert_raise(ArgumentError) do + @client.options["protocol.http.proxy"] = 'ftp://foo:8080' + end ensure if Object.const_defined?('HTTPAccess2') HTTPAccess2::Client::NO_PROXY_HOSTS.replace(backup) diff --git a/test/wsdl/map/map.wsdl b/test/wsdl/map/map.wsdl index 7b1a140827..e418a4cbbd 100644 --- a/test/wsdl/map/map.wsdl +++ b/test/wsdl/map/map.wsdl @@ -1,9 +1,9 @@ <?xml version="1.0"?> <definitions - name="RAA" - targetNamespace="http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/" - xmlns:tns="http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/" - xmlns:txd="http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/" + name="map" + targetNamespace="urn:map" + xmlns:tns="urn:map" + xmlns:txd="urn:map" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" @@ -19,8 +19,8 @@ <element name="item" minOccurs="0" maxOccurs="unbounded"> <complexType> <sequence> - <element name="key" type="anyType" /> - <element name="value" type="anyType" /> + <element name="key" type="xsd:anyType" /> + <element name="value" type="xsd:anyType" /> </sequence> </complexType> </element> @@ -34,32 +34,58 @@ <part name="return" type="apachesoap:Map"/> </message> - <portType name="RAABaseServicePortType"> + <message name="map2Request"> + <part name="arg" type="apachesoap:Map"/> + </message> + <message name="map2Response"> + <part name="return" type="apachesoap:Map"/> + </message> + + <portType name="MapServicePortType"> <operation name="map" parameterOrder=""> <input message="tns:mapRequest"/> <output message="tns:mapResponse"/> </operation> + + <operation name="map2" parameterOrder=""> + <input message="tns:map2Request"/> + <output message="tns:map2Response"/> + </operation> </portType> - <binding name="RAABaseServicePortBinding" type="tns:RAABaseServicePortType"> + <binding name="MapServicePortBinding" type="tns:MapServicePortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="map"> <soap:operation soapAction=""/> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" - namespace="http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/"/> + namespace="urn:map"/> + </input> + <output> + <soap:body use="encoded" + encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" + namespace="urn:map"/> + </output> + </operation> + + <operation name="map2"> + <soap:operation soapAction=""/> + <input> + <soap:body use="encoded" + encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" + namespace="urn:map"/> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" - namespace="http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/"/> + namespace="urn:map"/> </output> </operation> </binding> - <service name="RAAService"> - <port name="RAABaseServicePort" binding="tns:RAABaseServicePortBinding"> + <service name="MapService"> + <port name="MapServicePort" binding="tns:MapServicePortBinding"> <soap:address location="http://raa.ruby-lang.org/soap/1.0.2/"/> </port> </service> diff --git a/test/wsdl/map/test_map.rb b/test/wsdl/map/test_map.rb index b0f2fb5a53..bee3a75892 100644 --- a/test/wsdl/map/test_map.rb +++ b/test/wsdl/map/test_map.rb @@ -1,15 +1,75 @@ require 'test/unit' -require 'soap/processor' -require 'soap/mapping' -require 'soap/rpc/element' -require 'wsdl/importer' +require 'soap/rpc/httpserver' +require 'soap/wsdlDriver' module WSDL class TestMap < Test::Unit::TestCase + Port = 17171 + DIR = File.dirname(File.expand_path(__FILE__)) + + class Server < ::SOAP::RPC::HTTPServer + def on_init + add_method(self, 'map') + add_method(self, 'map2', 'arg') + end + + def map + {1 => "a", 2 => "b"} + end + + def map2(arg) + arg + end + end + def setup + setup_server + setup_client + end + + def setup_server + @server = Server.new( + :Port => Port, + :AccessLog => [], + :SOAPDefaultNamespace => "urn:map" + ) + @server.level = Logger::Severity::ERROR + @t = Thread.new { + Thread.current.abort_on_exception = true + @server.start + } + while @server.status != :Running + sleep 0.1 + unless @t.alive? + @t.join + raise + end + end + end + + def setup_client + wsdl = File.join(DIR, 'map.wsdl') + @client = ::SOAP::WSDLDriverFactory.new(wsdl).create_driver + @client.endpoint_url = "http://localhost:#{Port}/" + @client.generate_explicit_type = true + end + + def teardown + teardown_server + teardown_client + end + + def teardown_server + @server.shutdown + @t.kill + @t.join + end + + def teardown_client + @client.reset_stream end def test_by_wsdl @@ -31,6 +91,13 @@ class TestMap < Test::Unit::TestCase assert_equal(["b1"], map["b"]["b1"]) assert_equal(["b2"], map["b"]["b2"]) end + + def test_wsdldriver + assert_equal({1 => "a", 2 => "b"}, @client.map) + assert_equal({1 => 2}, @client.map2({1 => 2})) + assert_equal({1 => {2 => 3}}, @client.map2({1 => {2 => 3}})) + assert_equal({["a", 2] => {2 => 3}}, @client.map2({["a", 2] => {2 => 3}})) + end end diff --git a/test/xsd/test_xsd.rb b/test/xsd/test_xsd.rb index ce1b263625..1f594571fe 100644 --- a/test/xsd/test_xsd.rb +++ b/test/xsd/test_xsd.rb @@ -898,6 +898,99 @@ class TestXSD2 < Test::Unit::TestCase end end + def test_XSDNonPositiveInteger + o = XSD::XSDNonPositiveInteger.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::NonPositiveIntegerLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + -9999999999, + -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, + ] + targets.each do |int| + assert_equal(int, XSD::XSDNonPositiveInteger.new(int).data) + end + + targets = [ + "0", + "-9999999999", + "-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + ] + targets.each do |str| + assert_equal(str, XSD::XSDNonPositiveInteger.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDNonPositiveInteger.new(data).to_s) + end + + targets = [ + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDNonPositiveInteger.new(d) + end + end + end + + def test_XSDNegativeInteger + o = XSD::XSDNegativeInteger.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::NegativeIntegerLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + -1, + -9999999999, + -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, + ] + targets.each do |int| + assert_equal(int, XSD::XSDNegativeInteger.new(int).data) + end + + targets = [ + "-1", + "-9999999999", + "-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + ] + targets.each do |str| + assert_equal(str, XSD::XSDNegativeInteger.new(str).to_s) + end + + targets = [ + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDNegativeInteger.new(data).to_s) + end + + targets = [ + "-0.0", + "-5.2", + "-0.000000000000a", + "+-5", + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDNegativeInteger.new(d) + end + end + end + def test_XSDLong o = XSD::XSDLong.new assert_equal(XSD::Namespace, o.type.namespace) @@ -1005,6 +1098,407 @@ class TestXSD2 < Test::Unit::TestCase end end end + + def test_XSDShort + o = XSD::XSDShort.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::ShortLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 123, + -123, + 32767, + -32768, + ] + targets.each do |lng| + assert_equal(lng, XSD::XSDShort.new(lng).data) + end + + targets = [ + "0", + "123", + "-123", + "32767", + "-32768", + ] + targets.each do |str| + assert_equal(str, XSD::XSDShort.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDShort.new(data).to_s) + end + + targets = [ + 32768, + -32769, + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDShort.new(d) + end + end + end + + def test_XSDByte + o = XSD::XSDByte.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::ByteLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 123, + -123, + 127, + -128, + ] + targets.each do |lng| + assert_equal(lng, XSD::XSDByte.new(lng).data) + end + + targets = [ + "0", + "123", + "-123", + "127", + "-128", + ] + targets.each do |str| + assert_equal(str, XSD::XSDByte.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDByte.new(data).to_s) + end + + targets = [ + 128, + -129, + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDByte.new(d) + end + end + end + + def test_XSDNonNegativeInteger + o = XSD::XSDNonNegativeInteger.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::NonNegativeIntegerLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + ] + targets.each do |int| + assert_equal(int, XSD::XSDNonNegativeInteger.new(int).data) + end + + targets = [ + "0", + "1000000000", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + ] + targets.each do |str| + assert_equal(str, XSD::XSDNonNegativeInteger.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + [ + "+12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + ], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDNonNegativeInteger.new(data).to_s) + end + + targets = [ + "0.0", + "0.000000000000a", + "+-5", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDNonNegativeInteger.new(d) + end + end + end + + def test_XSDUnsignedLong + o = XSD::XSDUnsignedLong.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::UnsignedLongLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + 18446744073709551615, + ] + targets.each do |int| + assert_equal(int, XSD::XSDUnsignedLong.new(int).data) + end + + targets = [ + "0", + "1000000000", + "18446744073709551615", + ] + targets.each do |str| + assert_equal(str, XSD::XSDUnsignedLong.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["+18446744073709551615", "18446744073709551615"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDUnsignedLong.new(data).to_s) + end + + targets = [ + "0.0", + "0.000000000000a", + "+-5", + "18446744073709551615." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDUnsignedLong.new(d) + end + end + end + + def test_XSDUnsignedInt + o = XSD::XSDUnsignedInt.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::UnsignedIntLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + 4294967295, + ] + targets.each do |int| + assert_equal(int, XSD::XSDUnsignedInt.new(int).data) + end + + targets = [ + "0", + "1000000000", + "4294967295", + ] + targets.each do |str| + assert_equal(str, XSD::XSDUnsignedInt.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["+4294967295", "4294967295"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDUnsignedInt.new(data).to_s) + end + + targets = [ + "0.0", + "0.000000000000a", + "+-5", + "4294967295." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDUnsignedInt.new(d) + end + end + end + + def test_XSDUnsignedShort + o = XSD::XSDUnsignedShort.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::UnsignedShortLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 10000, + 65535, + ] + targets.each do |int| + assert_equal(int, XSD::XSDUnsignedShort.new(int).data) + end + + targets = [ + "0", + "1000", + "65535", + ] + targets.each do |str| + assert_equal(str, XSD::XSDUnsignedShort.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["+65535", "65535"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDUnsignedShort.new(data).to_s) + end + + targets = [ + "0.0", + "0.000000000000a", + "+-5", + "65535." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDUnsignedShort.new(d) + end + end + end + + def test_XSDUnsignedByte + o = XSD::XSDUnsignedByte.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::UnsignedByteLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 10, + 255, + ] + targets.each do |int| + assert_equal(int, XSD::XSDUnsignedByte.new(int).data) + end + + targets = [ + "0", + "10", + "255", + ] + targets.each do |str| + assert_equal(str, XSD::XSDUnsignedByte.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["+255", "255"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDUnsignedByte.new(data).to_s) + end + + targets = [ + "0.0", + "0.000000000000a", + "+-5", + "255." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDUnsignedByte.new(d) + end + end + end + + def test_XSDPositiveInteger + o = XSD::XSDPositiveInteger.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::PositiveIntegerLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 1, + 1000000000, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + ] + targets.each do |int| + assert_equal(int, XSD::XSDPositiveInteger.new(int).data) + end + + targets = [ + "1", + "1000000000", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + ] + targets.each do |str| + assert_equal(str, XSD::XSDPositiveInteger.new(str).to_s) + end + + targets = [ + ["+1", "1"], + ["000123", "123"], + [ + "+12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + ], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDPositiveInteger.new(data).to_s) + end + + targets = [ + "1.0", + "1.000000000000a", + "+-5", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDPositiveInteger.new(d) + end + end + end end |