diff options
Diffstat (limited to 'actionwebservice/lib/action_web_service/protocol')
5 files changed, 118 insertions, 780 deletions
diff --git a/actionwebservice/lib/action_web_service/protocol/abstract.rb b/actionwebservice/lib/action_web_service/protocol/abstract.rb index 9199dfe33f..f628fc4aee 100644 --- a/actionwebservice/lib/action_web_service/protocol/abstract.rb +++ b/actionwebservice/lib/action_web_service/protocol/abstract.rb @@ -1,126 +1,28 @@ module ActionWebService # :nodoc: module Protocol # :nodoc: - CheckedMessage = :checked - UncheckedMessage = :unchecked - - class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc: - end - - class AbstractProtocol # :nodoc: - attr :container_class - - def initialize(container_class) - @container_class = container_class - end - - def unmarshal_request(protocol_request) - raise NotImplementedError - end - - def marshal_response(protocol_request, return_value) - raise NotImplementedError - end - - def marshal_exception(exception) - raise NotImplementedError - end - - def self.create_protocol_request(container_class, action_pack_request) - nil - end - - def self.create_protocol_client(api, protocol_name, endpoint_uri, options) - nil - end - end - - class AbstractProtocolMessage # :nodoc: - attr_accessor :signature - attr_accessor :return_signature - attr_accessor :type - attr :options - - def initialize(options={}) - @signature = @return_signature = nil - @options = options - @type = @options[:type] || CheckedMessage - end - - def signature=(value) - return if value.nil? - @signature = [] - value.each do |klass| - if klass.is_a?(Hash) - @signature << klass.values.shift - else - @signature << klass - end - end - @signature - end - - def checked? - @type == CheckedMessage - end - - def check_parameter_types(values, signature) - return unless checked? && signature - unless signature.length == values.length - raise(ProtocolError, "Signature and parameter lengths mismatch") - end - (1..signature.length).each do |i| - check_compatibility(signature[i-1], values[i-1].class) - end - end - - def check_compatibility(expected_class, received_class) - return if \ - (expected_class == TrueClass or expected_class == FalseClass) and \ - (received_class == TrueClass or received_class == FalseClass) - unless received_class.ancestors.include?(expected_class) or \ - expected_class.ancestors.include?(received_class) - raise(ProtocolError, "value of type #{received_class.name} is not " + - "compatible with expected type #{expected_class.name}") - end - end + class ProtocolError < ActionWebService::ActionWebServiceError end - class ProtocolRequest < AbstractProtocolMessage # :nodoc: + class Request attr :protocol - attr :raw_body + attr :method_name + attr :method_params + attr :service_name - attr_accessor :web_service_name - attr_accessor :public_method_name - attr_accessor :content_type - - def initialize(protocol, raw_body, web_service_name, public_method_name, content_type, options={}) - super(options) + def initialize(protocol, method_name, method_params, service_name) @protocol = protocol - @raw_body = raw_body - @web_service_name = web_service_name - @public_method_name = public_method_name - @content_type = content_type - end - - def unmarshal - @protocol.unmarshal_request(self) - end - - def marshal(return_value) - @protocol.marshal_response(self, return_value) + @method_name = method_name + @method_params = method_params + @service_name = service_name end end - class ProtocolResponse < AbstractProtocolMessage # :nodoc: - attr :protocol - attr :raw_body - - attr_accessor :content_type + class Response + attr :body + attr :content_type - def initialize(protocol, raw_body, content_type, options={}) - super(options) - @protocol = protocol - @raw_body = raw_body + def initialize(body, content_type) + @body = body @content_type = content_type end end diff --git a/actionwebservice/lib/action_web_service/protocol/discovery.rb b/actionwebservice/lib/action_web_service/protocol/discovery.rb new file mode 100644 index 0000000000..ab51958ed9 --- /dev/null +++ b/actionwebservice/lib/action_web_service/protocol/discovery.rb @@ -0,0 +1,37 @@ +module ActionWebService + module Protocol + module Discovery + def self.included(base) + base.extend(ClassMethods) + base.send(:include, ActionWebService::Protocol::Discovery::InstanceMethods) + end + + module ClassMethods + def register_protocol(klass) + write_inheritable_array("web_service_protocols", [klass]) + end + end + + module InstanceMethods + private + def discover_web_service_request(ap_request) + (self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol| + protocol = protocol.new + request = protocol.unmarshal_request(ap_request) + return request unless request.nil? + end + nil + end + + def create_web_service_client(api, protocol_name, endpoint_uri, options) + (self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol| + protocol = protocol.new + client = protocol.protocol_client(api, protocol_name, endpoint_uri, options) + return client unless client.nil? + end + nil + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/protocol/registry.rb b/actionwebservice/lib/action_web_service/protocol/registry.rb deleted file mode 100644 index 0173673556..0000000000 --- a/actionwebservice/lib/action_web_service/protocol/registry.rb +++ /dev/null @@ -1,55 +0,0 @@ -module ActionWebService # :nodoc: - module Protocol # :nodoc: - HeaderAndBody = :header_and_body - BodyOnly = :body_only - - module Registry # :nodoc: - def self.append_features(base) # :nodoc: - super - base.extend(ClassMethods) - base.send(:include, ActionWebService::Protocol::Registry::InstanceMethods) - end - - module ClassMethods # :nodoc: - def register_protocol(type, klass) # :nodoc: - case type - when HeaderAndBody - write_inheritable_array("header_and_body_protocols", [klass]) - when BodyOnly - write_inheritable_array("body_only_protocols", [klass]) - else - raise(ProtocolError, "unknown protocol type #{type}") - end - end - end - - module InstanceMethods # :nodoc: - private - def probe_request_protocol(action_pack_request) - (header_and_body_protocols + body_only_protocols).each do |protocol| - protocol_request = protocol.create_protocol_request(self.class, action_pack_request) - return protocol_request if protocol_request - end - raise(ProtocolError, "unsupported request message format") - end - - def probe_protocol_client(api, protocol_name, endpoint_uri, options) - (header_and_body_protocols + body_only_protocols).each do |protocol| - protocol_client = protocol.create_protocol_client(api, protocol_name, endpoint_uri, options) - return protocol_client if protocol_client - end - raise(ProtocolError, "unsupported client protocol :#{protocol_name}") - end - - def header_and_body_protocols - self.class.read_inheritable_attribute("header_and_body_protocols") || [] - end - - def body_only_protocols - self.class.read_inheritable_attribute("body_only_protocols") || [] - end - end - - end - end -end diff --git a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb index 3c527fea93..f2e761f431 100644 --- a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb +++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb @@ -1,127 +1,49 @@ -require 'soap/processor' -require 'soap/mapping' -require 'soap/rpc/element' -require 'xsd/datatypes' -require 'xsd/ns' -require 'singleton' - -module ActionWebService # :nodoc: - module Protocol # :nodoc: - module Soap # :nodoc: - class ProtocolError < ActionWebService::ActionWebServiceError # :nodoc: - end - - def self.append_features(base) # :nodoc: - super - base.register_protocol(HeaderAndBody, SoapProtocol) - base.extend(ClassMethods) - base.wsdl_service_name('ActionWebService') - end - - module ClassMethods - # Specifies the WSDL service name to use when generating WSDL. Highly - # recommended that you set this value, or code generators may generate - # classes with very generic names. - # - # === Example - # class MyController < ActionController::Base - # wsdl_service_name 'MyService' - # end - def wsdl_service_name(name) - write_inheritable_attribute("soap_mapper", SoapMapper.new("urn:#{name}")) - end - - def soap_mapper # :nodoc: - read_inheritable_attribute("soap_mapper") - end +module ActionWebService + module Protocol + module Soap + def self.included(base) + base.register_protocol(SoapProtocol) + base.class_inheritable_option(:wsdl_service_name) end - - class SoapProtocol < AbstractProtocol # :nodoc: - attr :mapper - - def initialize(mapper) - @mapper = mapper - end - - def self.create_protocol_request(container_class, action_pack_request) - soap_action = extract_soap_action(action_pack_request) - return nil unless soap_action - service_name = action_pack_request.parameters['action'] - public_method_name = soap_action.gsub(/^[\/]+/, '').split(/[\/]+/)[-1] - content_type = action_pack_request.env['HTTP_CONTENT_TYPE'] - content_type ||= 'text/xml' - protocol = SoapProtocol.new(container_class.soap_mapper) - ProtocolRequest.new(protocol, - action_pack_request.raw_post, - service_name.to_sym, - public_method_name, - content_type) - end - - def self.create_protocol_client(api, protocol_name, endpoint_uri, options) - return nil unless protocol_name.to_s.downcase.to_sym == :soap - ActionWebService::Client::Soap.new(api, endpoint_uri, options) - end - - def unmarshal_request(protocol_request) - unmarshal = lambda do - envelope = SOAP::Processor.unmarshal(protocol_request.raw_body) - request = envelope.body.request - values = request.collect{|k, v| request[k]} - soap_to_ruby_array(values) - end - signature = protocol_request.signature - if signature - map_signature_types(signature) - values = unmarshal.call - signature = signature.map{|x|mapper.lookup(x).ruby_klass} - protocol_request.check_parameter_types(values, signature) - values + + class SoapProtocol + def initialize + @encoder = WS::Encoding::SoapRpcEncoding.new + @marshaler = WS::Marshaling::SoapMarshaler.new + end + + def unmarshal_request(ap_request) + return nil unless has_valid_soap_action?(ap_request) + method_name, params = @encoder.decode_rpc_call(ap_request.raw_post) + params = params.map{|x| @marshaler.unmarshal(x)} + service_name = ap_request.parameters['action'] + Request.new(self, method_name, params, service_name) + end + + def marshal_response(method_name, return_value, signature_type) + if !return_value.nil? && signature_type + type_binding = @marshaler.register_type(signature_type) + info = WS::ParamInfo.create(signature_type, 0, type_binding) + return_value = @marshaler.marshal(WS::Param.new(return_value, info)) else - if protocol_request.checked? - [] - else - unmarshal.call - end + return_value = nil end + body = @encoder.encode_rpc_response(method_name, return_value) + Response.new(body, 'text/xml') end - def marshal_response(protocol_request, return_value) - marshal = lambda do |signature| - mapping = mapper.lookup(signature[0]) - return_value = fixup_array_types(mapping, return_value) - signature = signature.map{|x|mapper.lookup(x).ruby_klass} - protocol_request.check_parameter_types([return_value], signature) - param_def = [['retval', 'return', mapping.registry_mapping]] - [param_def, ruby_to_soap(return_value)] - end - signature = protocol_request.return_signature - param_def = nil - if signature - param_def, return_value = marshal.call(signature) - else - if protocol_request.checked? - param_def, return_value = nil, nil - else - param_def, return_value = marshal.call([return_value.class]) - end - end - qname = XSD::QName.new(mapper.custom_namespace, - protocol_request.public_method_name) - response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def) - response.retval = return_value unless return_value.nil? - ProtocolResponse.new(self, create_response(response), 'text/xml') + def register_signature_type(spec) + @marshaler.register_type(spec) end - def marshal_exception(exc) - ProtocolResponse.new(self, create_exception_response(exc), 'text/xml') + def protocol_client(api, protocol_name, endpoint_uri, options) + return nil unless protocol_name == :soap + ActionWebService::Client::Soap.new(api, endpoint_uri, options) end private - def self.extract_soap_action(request) + def has_valid_soap_action?(request) return nil unless request.method == :post - content_type = request.env['HTTP_CONTENT_TYPE'] || 'text/xml' - return nil unless content_type soap_action = request.env['HTTP_SOAPACTION'] return nil unless soap_action soap_action.gsub!(/^"/, '') @@ -130,355 +52,7 @@ module ActionWebService # :nodoc: return nil if soap_action.empty? soap_action end - - def fixup_array_types(mapping, obj) - mapping.each_attribute do |name, type, attr_mapping| - if attr_mapping.custom_type? - attr_obj = obj.send(name) - new_obj = fixup_array_types(attr_mapping, attr_obj) - obj.send("#{name}=", new_obj) unless new_obj.equal?(attr_obj) - end - end - if mapping.is_a?(SoapArrayMapping) - obj = mapping.ruby_klass.new(obj) - # man, this is going to be slow for big arrays :( - (1..obj.size).each do |i| - i -= 1 - obj[i] = fixup_array_types(mapping.element_mapping, obj[i]) - end - else - if !mapping.generated_klass.nil? && mapping.generated_klass.respond_to?(:members) - # have to map the publically visible structure of the class - new_obj = mapping.generated_klass.new - mapping.generated_klass.members.each do |name, klass| - new_obj.send("#{name}=", obj.send(name)) - end - obj = new_obj - end - end - obj - end - - def map_signature_types(types) - types.collect{|type| mapper.map(type)} - end - - def create_response(body) - header = SOAP::SOAPHeader.new - body = SOAP::SOAPBody.new(body) - envelope = SOAP::SOAPEnvelope.new(header, body) - SOAP::Processor.marshal(envelope) - end - - def create_exception_response(exc) - detail = SOAP::Mapping::SOAPException.new(exc) - body = SOAP::SOAPFault.new( - SOAP::SOAPString.new('Server'), - SOAP::SOAPString.new(exc.to_s), - SOAP::SOAPString.new(self.class.name), - SOAP::Mapping.obj2soap(detail)) - create_response(body) - end - - def ruby_to_soap(obj) - SOAP::Mapping.obj2soap(obj, mapper.registry) - end - - def soap_to_ruby(obj) - SOAP::Mapping.soap2obj(obj, mapper.registry) - end - - def soap_to_ruby_array(array) - array.map{|x| soap_to_ruby(x)} - end - end - - class SoapMapper # :nodoc: - attr :registry - attr :custom_namespace - attr :custom_types - - def initialize(custom_namespace) - @custom_namespace = custom_namespace - @registry = SOAP::Mapping::Registry.new - @klass2map = {} - @custom_types = {} - @ar2klass = {} end - - def lookup(klass) - lookup_klass = klass.is_a?(Array) ? klass[0] : klass - generated_klass = nil - unless lookup_klass.respond_to?(:ancestors) - raise(ProtocolError, "expected parameter type definition to be a Class") - end - if lookup_klass.ancestors.include?(ActiveRecord::Base) - generated_klass = @ar2klass.has_key?(klass) ? @ar2klass[klass] : nil - klass = generated_klass if generated_klass - end - return @klass2map[klass] if @klass2map.has_key?(klass) - - custom_type = false - - ruby_klass = select_class(lookup_klass) - generated_klass = @ar2klass[lookup_klass] if @ar2klass.has_key?(lookup_klass) - type_name = ruby_klass.name - - # Array signatures generate a double-mapping and require generation - # of an Array subclass to represent the mapping in the SOAP - # registry - array_klass = nil - if klass.is_a?(Array) - array_klass = Class.new(Array) do - module_eval <<-END - def self.name - "#{type_name}Array" - end - END - end - end - - mapping = @registry.find_mapped_soap_class(ruby_klass) rescue nil - unless mapping - # Custom structured type, generate a mapping - info = { :type => XSD::QName.new(@custom_namespace, type_name) } - @registry.add(ruby_klass, - SOAP::SOAPStruct, - SOAP::Mapping::Registry::TypedStructFactory, - info) - mapping = ensure_mapped(ruby_klass) - custom_type = true - end - - array_mapping = nil - if array_klass - # Typed array always requires a custom type. The info of the array - # is the info of its element type (in mapping[2]), falling back - # to SOAP base types. - info = mapping[2] - info ||= {} - info[:type] ||= soap_base_type_qname(mapping[0]) - @registry.add(array_klass, - SOAP::SOAPArray, - SOAP::Mapping::Registry::TypedArrayFactory, - info) - array_mapping = ensure_mapped(array_klass) - end - - if array_mapping - @klass2map[ruby_klass] = SoapMapping.new(self, - type_name, - ruby_klass, - generated_klass, - mapping[0], - mapping, - custom_type) - @klass2map[klass] = SoapArrayMapping.new(self, - type_name, - array_klass, - array_mapping[0], - array_mapping, - @klass2map[ruby_klass]) - @custom_types[klass] = @klass2map[klass] - @custom_types[ruby_klass] = @klass2map[ruby_klass] if custom_type - else - @klass2map[klass] = SoapMapping.new(self, - type_name, - ruby_klass, - generated_klass, - mapping[0], - mapping, - custom_type) - @custom_types[klass] = @klass2map[klass] if custom_type - end - - @klass2map[klass] - end - alias :map :lookup - - def map_container_services(container, &block) - dispatching_mode = container.web_service_dispatching_mode - web_services = nil - case dispatching_mode - when :direct - api = container.class.web_service_api - if container.respond_to?(:controller_class_name) - web_service_name = container.controller_class_name.sub(/Controller$/, '').underscore - else - web_service_name = container.class.name.demodulize.underscore - end - web_services = { web_service_name => api } - when :delegated - web_services = {} - container.class.web_services.each do |web_service_name, web_service_info| - begin - object = container.web_service_object(web_service_name) - rescue Exception => e - raise(ProtocolError, "failed to retrieve web service object for web service '#{web_service_name}': #{e.message}") - end - web_services[web_service_name] = object.class.web_service_api - end - end - web_services.each do |web_service_name, api| - if api.nil? - raise(ProtocolError, "no web service API set while in :#{dispatching_mode} mode") - end - map_api(api) do |api_methods| - yield web_service_name, api, api_methods if block_given? - end - end - end - - def map_api(api, &block) - lookup_proc = lambda do |klass| - mapping = lookup(klass) - custom_mapping = nil - if mapping.respond_to?(:element_mapping) - custom_mapping = mapping.element_mapping - else - custom_mapping = mapping - end - if custom_mapping && custom_mapping.custom_type? - # What gives? This is required so that structure types - # referenced only by structures (and not signatures) still - # have a custom type mapping in the registry (needed for WSDL - # generation). - custom_mapping.each_attribute{} - end - mapping - end - api_methods = block.nil?? nil : {} - api.api_methods.each do |method_name, method_info| - expects = method_info[:expects] - expects_signature = nil - if expects - expects_signature = block ? [] : nil - expects.each do |klass| - lookup_klass = nil - if klass.is_a?(Hash) - lookup_klass = lookup_proc.call(klass.values[0]) - expects_signature << {klass.keys[0]=>lookup_klass} if block - else - lookup_klass = lookup_proc.call(klass) - expects_signature << lookup_klass if block - end - end - end - returns = method_info[:returns] - returns_signature = returns ? returns.map{|klass| lookup_proc.call(klass)} : nil - if block - api_methods[method_name] = { - :expects => expects_signature, - :returns => returns_signature - } - end - end - yield api_methods if block - end - - private - def select_class(klass) - return Integer if klass == Fixnum - if klass.ancestors.include?(ActiveRecord::Base) - new_klass = Class.new(ActionWebService::Struct) - new_klass.class_eval <<-EOS - def self.name - "#{klass.name}" - end - EOS - klass.columns.each do |column| - next if column.klass.nil? - new_klass.send(:member, column.name.to_sym, column.klass) - end - @ar2klass[klass] = new_klass - return new_klass - end - klass - end - - def ensure_mapped(klass) - mapping = @registry.find_mapped_soap_class(klass) rescue nil - raise(ProtocolError, "failed to register #{klass.name}") unless mapping - mapping - end - - def soap_base_type_qname(base_type) - xsd_type = base_type.ancestors.find{|c| c.const_defined? 'Type'} - xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type - end - end - - class SoapMapping # :nodoc: - attr :ruby_klass - attr :generated_klass - attr :soap_klass - attr :registry_mapping - - def initialize(mapper, type_name, ruby_klass, generated_klass, soap_klass, registry_mapping, - custom_type=false) - @mapper = mapper - @type_name = type_name - @ruby_klass = ruby_klass - @generated_klass = generated_klass - @soap_klass = soap_klass - @registry_mapping = registry_mapping - @custom_type = custom_type - end - - def type_name - @type_name - end - - def custom_type? - @custom_type - end - - def qualified_type_name - name = type_name - if custom_type? - "typens:#{name}" - else - xsd_type_for(@soap_klass) - end - end - - def each_attribute(&block) - if @ruby_klass.respond_to?(:members) - @ruby_klass.members.each do |name, klass| - name = name.to_s - mapping = @mapper.lookup(klass) - yield name, mapping.qualified_type_name, mapping - end - end - end - - def is_xsd_type?(klass) - klass.ancestors.include?(XSD::NSDBase) - end - - def xsd_type_for(klass) - ns = XSD::NS.new - ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag) - xsd_klass = klass.ancestors.find{|c| c.const_defined?('Type')} - return ns.name(XSD::AnyTypeName) unless xsd_klass - ns.name(xsd_klass.const_get('Type')) - end - end - - class SoapArrayMapping < SoapMapping # :nodoc: - attr :element_mapping - - def initialize(mapper, type_name, ruby_klass, soap_klass, registry_mapping, element_mapping) - super(mapper, type_name, ruby_klass, nil, soap_klass, registry_mapping, true) - @element_mapping = element_mapping - end - - def type_name - super + "Array" - end - - def each_attribute(&block); end - end end end end diff --git a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb index 1addccba56..1533a2bdb9 100644 --- a/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb +++ b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb @@ -1,167 +1,47 @@ -require 'xmlrpc/parser' -require 'xmlrpc/create' -require 'xmlrpc/config' -require 'xmlrpc/utils' -require 'singleton' - -module XMLRPC # :nodoc: - class XmlRpcHelper # :nodoc: - include Singleton - include ParserWriterChooseMixin - - def parse_method_call(message) - parser().parseMethodCall(message) - end - - def create_method_response(successful, return_value) - create().methodResponse(successful, return_value) - end - end -end - -module ActionWebService # :nodoc: - module Protocol # :nodoc: - module XmlRpc # :nodoc: - def self.append_features(base) # :nodoc: - super - base.register_protocol(BodyOnly, XmlRpcProtocol) +module ActionWebService + module Protocol + module XmlRpc + def self.included(base) + base.register_protocol(XmlRpcProtocol) end + + class XmlRpcProtocol + attr :marshaler - class XmlRpcProtocol < AbstractProtocol # :nodoc: - def self.create_protocol_request(container_class, action_pack_request) - helper = XMLRPC::XmlRpcHelper.instance - service_name = action_pack_request.parameters['action'] - methodname, params = helper.parse_method_call(action_pack_request.raw_post) - methodname.gsub!(/^[^\.]+\./, '') unless methodname =~ /^system\./ # XXX - protocol = XmlRpcProtocol.new(container_class) - content_type = action_pack_request.env['HTTP_CONTENT_TYPE'] - content_type ||= 'text/xml' - request = ProtocolRequest.new(protocol, - action_pack_request.raw_post, - service_name.to_sym, - methodname, - content_type, - :xmlrpc_values => params) - request - rescue - nil - end - - def self.create_protocol_client(api, protocol_name, endpoint_uri, options) - return nil unless protocol_name.to_s.downcase.to_sym == :xmlrpc - ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options) + def initialize + @encoder = WS::Encoding::XmlRpcEncoding.new + @marshaler = WS::Marshaling::XmlRpcMarshaler.new end - def initialize(container_class) - super(container_class) + def unmarshal_request(ap_request) + method_name, params = @encoder.decode_rpc_call(ap_request.raw_post) + params = params.map{|x| @marshaler.unmarshal(x)} + service_name = ap_request.parameters['action'] + Request.new(self, method_name, params, service_name) + rescue + nil end - def unmarshal_request(protocol_request) - values = protocol_request.options[:xmlrpc_values] - signature = protocol_request.signature - if signature - values = self.class.transform_incoming_method_params(self.class.transform_array_types(signature), values) - protocol_request.check_parameter_types(values, check_array_types(signature)) - values - else - protocol_request.checked? ? [] : values - end - end - - def marshal_response(protocol_request, return_value) - helper = XMLRPC::XmlRpcHelper.instance - signature = protocol_request.return_signature - if signature - protocol_request.check_parameter_types([return_value], check_array_types(signature)) - return_value = self.class.transform_return_value(self.class.transform_array_types(signature), return_value) - raw_response = helper.create_method_response(true, return_value) + def marshal_response(method_name, return_value, signature_type) + if !return_value.nil? && signature_type + type_binding = @marshaler.register_type(signature_type) + info = WS::ParamInfo.create(signature_type, 0, type_binding) + return_value = @marshaler.marshal(WS::Param.new(return_value, info)) else - # XML-RPC doesn't have the concept of a void method, nor does it - # support a nil return value, so return true if we would have returned - # nil - if protocol_request.checked? - raw_response = helper.create_method_response(true, true) - else - return_value = true if return_value.nil? - raw_response = helper.create_method_response(true, return_value) - end + return_value = nil end - ProtocolResponse.new(self, raw_response, 'text/xml') + body = @encoder.encode_rpc_response(method_name, return_value) + Response.new(body, 'text/xml') end - - def marshal_exception(exception) - helper = XMLRPC::XmlRpcHelper.instance - exception = XMLRPC::FaultException.new(1, exception.message) - raw_response = helper.create_method_response(false, exception) - ProtocolResponse.new(self, raw_response, 'text/xml') - end - - class << self - def transform_incoming_method_params(signature, params) - (1..signature.size).each do |i| - i -= 1 - params[i] = xmlrpc_to_ruby(params[i], signature[i]) - end - params - end - - def transform_return_value(signature, return_value) - ruby_to_xmlrpc(return_value, signature[0]) - end - - def ruby_to_xmlrpc(param, param_class) - if param_class.is_a?(XmlRpcArray) - param.map{|p| ruby_to_xmlrpc(p, param_class.klass)} - elsif param_class.ancestors.include?(ActiveRecord::Base) - param.instance_variable_get('@attributes') - elsif param_class.ancestors.include?(ActionWebService::Struct) - struct = {} - param_class.members.each do |name, klass| - value = param.send(name) - next if value.nil? - struct[name.to_s] = value - end - struct - else - param - end - end - def xmlrpc_to_ruby(param, param_class) - if param_class.is_a?(XmlRpcArray) - param.map{|p| xmlrpc_to_ruby(p, param_class.klass)} - elsif param_class.ancestors.include?(ActiveRecord::Base) - raise(ProtocolError, "incoming ActiveRecord::Base types are not allowed") - elsif param_class.ancestors.include?(ActionWebService::Struct) - unless param.is_a?(Hash) - raise(ProtocolError, "expected parameter to be a Hash") - end - new_param = param_class.new - param_class.members.each do |name, klass| - new_param.send('%s=' % name.to_s, param[name.to_s]) - end - new_param - else - param - end - end + def register_signature_type(spec) + nil + end - def transform_array_types(signature) - signature.map{|x| x.is_a?(Array) ? XmlRpcArray.new(x[0]) : x} - end + def protocol_client(api, protocol_name, endpoint_uri, options) + return nil unless protocol_name == :xmlrpc + ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options) end - - private - def check_array_types(signature) - signature.map{|x| x.is_a?(Array) ? Array : x} - end - - class XmlRpcArray - attr :klass - def initialize(klass) - @klass = klass - end - end end end end |