diff options
Diffstat (limited to 'actionwebservice')
24 files changed, 404 insertions, 551 deletions
diff --git a/actionwebservice/ChangeLog b/actionwebservice/ChangeLog index c9803bd2b7..4efc01f46a 100644 --- a/actionwebservice/ChangeLog +++ b/actionwebservice/ChangeLog @@ -1,8 +1,11 @@ UNRELEASED - * lib/*,test/*,examples/*: prefix all generic "service" - type names with web_. update all using code as well as - the RDoc. + * lib/action_service/dispatcher*: replaces "router" fragments with + one file for Action Controllers, moves dispatching work out of + the container + * lib/*,test/*,examples/*: rename project to + ActionWebService. prefix all generic "service" type names with web_. + update all using code as well as the RDoc. * lib/action_service/router/wsdl.rb: ensure that #wsdl is defined in the final container class, or the new ActionPack filtering will exclude it diff --git a/actionwebservice/HACKING b/actionwebservice/HACKING deleted file mode 100644 index 536aac7a24..0000000000 --- a/actionwebservice/HACKING +++ /dev/null @@ -1,44 +0,0 @@ -== Coding Style - -Please try to follow Rails conventions and idioms. - - -== Concepts - - * Service - A service has an associated API definition, and - implements the methods defined in the API definition - - * Container - A container contains zero or more services - - * API - An API definition defines a list of methods implemented by - a service - - * Router - A router takes raw wire requests, decodes them, performs the invocation on - the service, and generates raw wire responses from the invocation result. - A router is mixed into a container class. - - * Protocol - A protocol implementation implements the unmarshaling and marshaling of - raw wire requests and responses. Registers with router. - - -== Action Pack Integration - -For Action Pack, the ActionController is both container and router, and also contains -the protocol implementations. - - -== Adding support for a new protocol - - 1. Add an ActionWebService::Protocol::YourProtocol module and any classes you need to - perform unmarshaling/marshaling of protocol requests. See the SOAP implementation - for an example of a complex mapping, and also see - ActionWebService::Protocol::AbstractProtocol for the methods you need to implement. - - 2. Add unit tests for your new protocol. Be sure to test using a Action Pack test request - duplicating how the real requests will arrive and verify that mapping to and from Ruby - types works correctly. diff --git a/actionwebservice/Rakefile b/actionwebservice/Rakefile index 49c3409e60..3e57e48d88 100644 --- a/actionwebservice/Rakefile +++ b/actionwebservice/Rakefile @@ -36,7 +36,7 @@ Rake::RDocTask.new { |rdoc| rdoc.rdoc_files.include('lib/action_web_service/api/*.rb') rdoc.rdoc_files.include('lib/action_web_service/client/*.rb') rdoc.rdoc_files.include('lib/action_web_service/protocol/*.rb') - rdoc.rdoc_files.include('lib/action_web_service/router/*.rb') + rdoc.rdoc_files.include('lib/action_web_service/dispatcher/*.rb') rdoc.rdoc_files.include('lib/action_web_service/support/*.rb') } @@ -63,7 +63,7 @@ spec = Gem::Specification.new do |s| s.require_path = 'lib' s.autorequire = 'action_web_service' - s.files = [ "Rakefile", "setup.rb", "README", "TODO", "HACKING", "ChangeLog", "MIT-LICENSE" ] + s.files = [ "Rakefile", "setup.rb", "README", "TODO", "ChangeLog", "MIT-LICENSE" ] s.files = s.files + Dir.glob( "examples/**/*" ).delete_if { |item| item.include?( "\.svn" ) } s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) } s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) } diff --git a/actionwebservice/TODO b/actionwebservice/TODO index e753fbb1d7..35c6c66f11 100644 --- a/actionwebservice/TODO +++ b/actionwebservice/TODO @@ -1,8 +1,11 @@ = 0.4.0 Tasks - add ActiveRecord-like logging that includes timing information - - rename project to 'actionwebservice', Action Web Service = Post-0.4.0 Tasks + - support namespaced custom types in WSDL in a way that interoperates + with .NET (.NET croaks on '::' currently). perhaps a transform + that maps Ruby::Class to Ruby.Class and back. + - relax type-checking for XML-RPC, and perform casts between base types if there are mismatches (i.e. String received when Integer expected, or vice-versa) @@ -23,12 +26,8 @@ web_service :xmlrpc { BloggingServices.new(@request) } end - - supported namespaced custom types in WSDL in a way that interoperates - with .NET (.NET croaks on '::' currently) - - - simplification: collapse Router::ActionController, Router::Wsdl - and API::ActionController into Container::ActionController. - the seperation has gained us nothing. + - verify that cookie support works, and add cookie-authenticated + service examples. test with .NET. = Low priority tasks - add better type mapping tests for XML-RPC diff --git a/actionwebservice/lib/action_web_service.rb b/actionwebservice/lib/action_web_service.rb index a55afc2244..5cf988a0f8 100644 --- a/actionwebservice/lib/action_web_service.rb +++ b/actionwebservice/lib/action_web_service.rb @@ -41,7 +41,7 @@ require 'action_web_service/api' require 'action_web_service/struct' require 'action_web_service/container' require 'action_web_service/protocol' -require 'action_web_service/router' +require 'action_web_service/dispatcher' ActionWebService::Base.class_eval do include ActionWebService::API @@ -55,6 +55,6 @@ ActionController::Base.class_eval do include ActionWebService::Protocol::XmlRpc include ActionWebService::API include ActionWebService::API::ActionController - include ActionWebService::Router::ActionController - include ActionWebService::Router::Wsdl + include ActionWebService::Dispatcher + include ActionWebService::Dispatcher::ActionController end diff --git a/actionwebservice/lib/action_web_service/client.rb b/actionwebservice/lib/action_web_service/client.rb index 77f934882c..2a1e33054d 100644 --- a/actionwebservice/lib/action_web_service/client.rb +++ b/actionwebservice/lib/action_web_service/client.rb @@ -1,3 +1,3 @@ require 'action_web_service/client/base' -require 'action_web_service/client/soap' -require 'action_web_service/client/xmlrpc' +require 'action_web_service/client/soap_client' +require 'action_web_service/client/xmlrpc_client' diff --git a/actionwebservice/lib/action_web_service/client/soap.rb b/actionwebservice/lib/action_web_service/client/soap_client.rb index 3557f88594..3557f88594 100644 --- a/actionwebservice/lib/action_web_service/client/soap.rb +++ b/actionwebservice/lib/action_web_service/client/soap_client.rb diff --git a/actionwebservice/lib/action_web_service/client/xmlrpc.rb b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb index df51230b81..df51230b81 100644 --- a/actionwebservice/lib/action_web_service/client/xmlrpc.rb +++ b/actionwebservice/lib/action_web_service/client/xmlrpc_client.rb diff --git a/actionwebservice/lib/action_web_service/container.rb b/actionwebservice/lib/action_web_service/container.rb index 6fa14def56..f02717579e 100644 --- a/actionwebservice/lib/action_web_service/container.rb +++ b/actionwebservice/lib/action_web_service/container.rb @@ -5,8 +5,6 @@ module ActionWebService # :nodoc: def self.append_features(base) # :nodoc: super - base.class_inheritable_option(:web_service_dispatching_mode, :direct) - base.class_inheritable_option(:web_service_exception_reporting, true) base.extend(ClassMethods) base.send(:include, ActionWebService::Container::InstanceMethods) end @@ -82,151 +80,6 @@ module ActionWebService # :nodoc: service = info[:block] service ? instance_eval(&service) : info[:object] end - - private - def dispatch_web_service_request(protocol_request) - case web_service_dispatching_mode - when :direct - dispatch_direct_web_service_request(protocol_request) - when :delegated - dispatch_delegated_web_service_request(protocol_request) - else - raise(ContainerError, "unsupported dispatching mode :#{web_service_dispatching_mode}") - end - end - - def dispatch_direct_web_service_request(protocol_request) - public_method_name = protocol_request.public_method_name - api = self.class.web_service_api - method_name = api.api_method_name(public_method_name) - block = nil - expects = nil - if method_name - signature = api.api_methods[method_name] - expects = signature[:expects] - protocol_request.type = Protocol::CheckedMessage - protocol_request.signature = expects - protocol_request.return_signature = signature[:returns] - else - protocol_request.type = Protocol::UncheckedMessage - system_methods = self.class.read_inheritable_attribute('default_system_methods') || {} - protocol = protocol_request.protocol - block = system_methods[protocol.class] - unless block - method_name = api.default_api_method - unless method_name && respond_to?(method_name) - raise(ContainerError, "no such method ##{public_method_name}") - end - end - end - - @method_params = protocol_request.unmarshal - @params ||= {} - if expects - (1..@method_params.size).each do |i| - i -= 1 - if expects[i].is_a?(Hash) - @params[expects[i].keys.shift.to_s] = @method_params[i] - else - @params["param#{i}"] = @method_params[i] - end - end - end - - if respond_to?(:before_action) - @params['action'] = method_name.to_s - return protocol_request.marshal(nil) if before_action == false - end - - perform_invoke = lambda do - if block - block.call(public_method_name, self.class, *@method_params) - else - send(method_name) - end - end - try_default = true - result = nil - catch(:try_default) do - result = perform_invoke.call - try_default = false - end - if try_default - method_name = api.default_api_method - if method_name - protocol_request.type = Protocol::UncheckedMessage - else - raise(ContainerError, "no such method ##{public_method_name}") - end - result = perform_invoke.call - end - after_action if respond_to?(:after_action) - protocol_request.marshal(result) - end - - def dispatch_delegated_web_service_request(protocol_request) - web_service_name = protocol_request.web_service_name - service = web_service_object(web_service_name) - api = service.class.web_service_api - public_method_name = protocol_request.public_method_name - method_name = api.api_method_name(public_method_name) - - invocation = ActionWebService::Invocation::InvocationRequest.new( - ActionWebService::Invocation::ConcreteInvocation, - public_method_name, - method_name) - - if method_name - protocol_request.type = Protocol::CheckedMessage - signature = api.api_methods[method_name] - protocol_request.signature = signature[:expects] - protocol_request.return_signature = signature[:returns] - invocation.params = protocol_request.unmarshal - else - protocol_request.type = Protocol::UncheckedMessage - invocation.type = ActionWebService::Invocation::VirtualInvocation - system_methods = self.class.read_inheritable_attribute('default_system_methods') || {} - protocol = protocol_request.protocol - block = system_methods[protocol.class] - if block - invocation.block = block - invocation.block_params << service.class - else - method_name = api.default_api_method - if method_name && service.respond_to?(method_name) - invocation.params = protocol_request.unmarshal - invocation.method_name = method_name.to_sym - else - raise(ContainerError, "no such method /#{web_service_name}##{public_method_name}") - end - end - end - - canceled_reason = nil - canceled_block = lambda{|r| canceled_reason = r} - perform_invoke = lambda do - service.perform_invocation(invocation, &canceled_block) - end - try_default = true - result = nil - catch(:try_default) do - result = perform_invoke.call - try_default = false - end - if try_default - method_name = api.default_api_method - if method_name - protocol_request.type = Protocol::UncheckedMessage - invocation.params = protocol_request.unmarshal - invocation.method_name = method_name.to_sym - invocation.type = ActionWebService::Invocation::UnpublishedConcreteInvocation - else - raise(ContainerError, "no such method /#{web_service_name}##{public_method_name}") - end - result = perform_invoke.call - end - protocol_request.marshal(result) - end end end end diff --git a/actionwebservice/lib/action_web_service/dispatcher.rb b/actionwebservice/lib/action_web_service/dispatcher.rb new file mode 100644 index 0000000000..601d831373 --- /dev/null +++ b/actionwebservice/lib/action_web_service/dispatcher.rb @@ -0,0 +1,2 @@ +require 'action_web_service/dispatcher/abstract' +require 'action_web_service/dispatcher/action_controller_dispatcher' diff --git a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb new file mode 100644 index 0000000000..e03446924a --- /dev/null +++ b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb @@ -0,0 +1,158 @@ +require 'benchmark' + +module ActionWebService # :nodoc: + module Dispatcher # :nodoc: + class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc: + end + + def self.append_features(base) # :nodoc: + super + base.class_inheritable_option(:web_service_dispatching_mode, :direct) + base.class_inheritable_option(:web_service_exception_reporting, true) + base.send(:include, ActionWebService::Dispatcher::InstanceMethods) + end + + module InstanceMethods # :nodoc: + private + def dispatch_web_service_request(action_pack_request) + protocol_request = protocol_response = nil + bm = Benchmark.measure do + protocol_request = probe_request_protocol(action_pack_request) + protocol_response = dispatch_protocol_request(protocol_request) + end + [protocol_request, protocol_response, bm.real, nil] + rescue Exception => e + protocol_response = prepare_exception_response(protocol_request, e) + [protocol_request, prepare_exception_response(protocol_request, e), nil, e] + end + + def dispatch_protocol_request(protocol_request) + case web_service_dispatching_mode + when :direct + dispatch_direct_request(protocol_request) + when :delegated + dispatch_delegated_request(protocol_request) + else + raise(ContainerError, "unsupported dispatching mode :#{web_service_dispatching_mode}") + end + end + + def dispatch_direct_request(protocol_request) + request = prepare_dispatch_request(protocol_request) + return_value = direct_invoke(request) + protocol_request.marshal(return_value) + end + + def dispatch_delegated_request(protocol_request) + request = prepare_dispatch_request(protocol_request) + return_value = delegated_invoke(request) + protocol_request.marshal(return_value) + end + + def direct_invoke(request) + return nil unless before_direct_invoke(request) + return_value = send(request.method_name) + after_direct_invoke(request) + return_value + end + + def before_direct_invoke(request) + @method_params = request.params + end + + def after_direct_invoke(request) + end + + def delegated_invoke(request) + cancellation_reason = nil + web_service = request.web_service + return_value = web_service.perform_invocation(request.method_name, request.params) do |x| + cancellation_reason = x + end + if cancellation_reason + raise(DispatcherError, "request canceled: #{cancellation_reason}") + end + return_value + end + + def fallback_invoke(dispatch_request) + raise NotImplementedError + end + + def prepare_dispatch_request(protocol_request) + api = method_name = web_service_name = web_service = params = nil + public_method_name = protocol_request.public_method_name + case web_service_dispatching_mode + when :direct + api = self.class.web_service_api + when :delegated + web_service_name = protocol_request.web_service_name + web_service = web_service_object(web_service_name) + api = web_service.class.web_service_api + end + method_name = api.api_method_name(public_method_name) + signature = nil + if method_name + signature = api.api_methods[method_name] + protocol_request.type = Protocol::CheckedMessage + protocol_request.signature = signature[:expects] + protocol_request.return_signature = signature[:returns] + else + method_name = api.default_api_method + if method_name + protocol_request.type = Protocol::UncheckedMessage + else + raise(DispatcherError, "no such method #{web_service_name}##{public_method_name}") + end + end + params = protocol_request.unmarshal + DispatchRequest.new( + :api => api, + :public_method_name => public_method_name, + :method_name => method_name, + :signature => signature, + :web_service_name => web_service_name, + :web_service => web_service, + :params => params) + end + + def prepare_exception_response(protocol_request, exception) + if protocol_request && exception + case web_service_dispatching_mode + when :direct + if web_service_exception_reporting + return protocol_request.protocol.marshal_exception(exception) + else + raise exception + end + when :delegated + web_service = web_service_object(protocol_request.web_service_name) + if web_service && web_service.class.web_service_exception_reporting + return protocol_request.protocol.marshal_exception(exception) + else + raise exception + end + end + else + protocol_request.protocol.marshal_exception(RuntimeError.new("missing protocol request or exception")) + end + rescue Exception + nil + end + + class DispatchRequest + attr :api + attr :public_method_name + attr :method_name + attr :signature + attr :web_service_name + attr :web_service + attr :params + + def initialize(values={}) + values.each{|k,v| instance_variable_set("@#{k.to_s}", v)} + end + end + end + end +end diff --git a/actionwebservice/lib/action_web_service/router/wsdl.rb b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb index 6963334818..68c0f4ffb3 100644 --- a/actionwebservice/lib/action_web_service/router/wsdl.rb +++ b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb @@ -1,23 +1,114 @@ module ActionWebService # :nodoc: - module Router # :nodoc: - module Wsdl # :nodoc: + module Dispatcher # :nodoc: + module ActionController # :nodoc: def self.append_features(base) # :nodoc: - base.class_eval do + super + base.class_eval do class << self - alias_method :inherited_without_wsdl, :inherited + alias_method :inherited_without_action_controller, :inherited + end + end + base.class_eval do + alias_method :before_direct_invoke_without_action_controller, :before_direct_invoke + alias_method :after_direct_invoke_without_action_controller, :after_direct_invoke + end + base.add_web_service_api_callback do |klass, api| + if klass.web_service_dispatching_mode == :direct + klass.class_eval <<-EOS + def api + controller_dispatch_web_service_request + end + EOS + end + end + base.add_web_service_definition_callback do |klass, name, info| + if klass.web_service_dispatching_mode == :delegated + klass.class_eval <<-EOS + def #{name} + controller_dispatch_web_service_request + end + EOS end end base.extend(ClassMethods) + base.send(:include, ActionWebService::Dispatcher::ActionController::Invocation) end - module ClassMethods + module ClassMethods # :nodoc: def inherited(child) - inherited_without_wsdl(child) - child.send(:include, ActionWebService::Router::Wsdl::InstanceMethods) + inherited_without_action_controller(child) + child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlGeneration) end end - module InstanceMethods # :nodoc: + module Invocation # :nodoc: + private + def controller_dispatch_web_service_request + request, response, elapsed, exception = dispatch_web_service_request(@request) + if response + begin + log_request(request) + log_error(exception) if exception && logger + log_response(response, elapsed) + response_options = { :type => response.content_type, :disposition => 'inline' } + send_data(response.raw_body, response_options) + rescue Exception => e + log_error(e) unless logger.nil? + render_text("Internal protocol error", "500 Internal Server Error") + end + else + logger.error("No response available") unless logger.nil? + render_text("Internal protocol error", "500 Internal Server Error") + end + end + + def before_direct_invoke(request) + before_direct_invoke_without_action_controller(request) + @params ||= {} + signature = request.signature + if signature && (expects = request.signature[:expects]) + (0..(@method_params.size-1)).each do |i| + if expects[i].is_a?(Hash) + @params[expects[i].keys[0].to_s] = @method_params[i] + else + @params['param%d' % i] = @method_params[i] + end + end + end + @params['action'] = request.method_name.to_s + @session ||= {} + @assigns ||= {} + return nil if before_action == false + true + end + + def after_direct_invoke(request) + after_direct_invoke_without_action_controller(request) + after_action + end + + def log_request(request) + unless logger.nil? || request.nil? + logger.debug("\nWeb Service Request:") + indented = request.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n") + logger.debug(indented) + end + end + + def log_response(response, elapsed) + unless logger.nil? || response.nil? + logger.debug("\nWeb Service Response (%f):" % elapsed) + indented = response.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n") + logger.debug(indented) + end + end + + unless method_defined?(:logger) + def logger; @logger; end + end + end + + module WsdlGeneration # :nodoc: XsdNs = 'http://www.w3.org/2001/XMLSchema' WsdlNs = 'http://schemas.xmlsoap.org/wsdl/' SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/' @@ -28,7 +119,7 @@ module ActionWebService # :nodoc: case @request.method when :get begin - host_name = @request.env['HTTP_HOST']||@request.env['SERVER_NAME'] + host_name = @request.env['HTTP_HOST'] || @request.env['SERVER_NAME'] uri = "http://#{host_name}/#{controller_name}/" soap_action_base = "/#{controller_name}" xml = to_wsdl(self, uri, soap_action_base) diff --git a/actionwebservice/lib/action_web_service/invocation.rb b/actionwebservice/lib/action_web_service/invocation.rb index 64d3e8d524..62f38b8ecd 100644 --- a/actionwebservice/lib/action_web_service/invocation.rb +++ b/actionwebservice/lib/action_web_service/invocation.rb @@ -1,9 +1,5 @@ module ActionWebService # :nodoc: module Invocation # :nodoc: - ConcreteInvocation = :concrete - VirtualInvocation = :virtual - UnpublishedConcreteInvocation = :unpublished_concrete - class InvocationError < ActionWebService::ActionWebServiceError # :nodoc: end @@ -137,31 +133,15 @@ module ActionWebService # :nodoc: end end - def perform_invocation_with_interception(invocation, &block) - return if before_invocation(invocation.method_name, invocation.params, &block) == false - result = perform_invocation_without_interception(invocation) - after_invocation(invocation.method_name, invocation.params, result) - result + def perform_invocation_with_interception(method_name, params, &block) + return if before_invocation(method_name, params, &block) == false + return_value = perform_invocation_without_interception(method_name, params) + after_invocation(method_name, params, return_value) + return_value end - def perform_invocation(invocation) - if invocation.concrete? - unless self.respond_to?(invocation.method_name) && \ - self.class.web_service_api.has_api_method?(invocation.method_name) - raise InvocationError, "no such web service method '#{invocation.method_name}' on service object" - end - end - params = invocation.params - if invocation.concrete? || invocation.unpublished_concrete? - self.send(invocation.method_name, *params) - else - if invocation.block - params = invocation.block_params + params - invocation.block.call(invocation.public_method_name, *params) - else - self.send(invocation.method_name, *params) - end - end + def perform_invocation(method_name, params) + send(method_name, *params) end def before_invocation(name, args, &block) @@ -221,32 +201,5 @@ module ActionWebService # :nodoc: end end end - - class InvocationRequest # :nodoc: - attr_accessor :type - attr :public_method_name - attr_accessor :method_name - attr_accessor :params - attr_accessor :block - attr :block_params - - def initialize(type, public_method_name, method_name, params=nil) - @type = type - @public_method_name = public_method_name - @method_name = method_name - @params = params || [] - @block = nil - @block_params = [] - end - - def concrete? - @type == ConcreteInvocation ? true : false - end - - def unpublished_concrete? - @type == UnpublishedConcreteInvocation ? true : false - end - end - end end diff --git a/actionwebservice/lib/action_web_service/protocol.rb b/actionwebservice/lib/action_web_service/protocol.rb index 733787136a..b15e850676 100644 --- a/actionwebservice/lib/action_web_service/protocol.rb +++ b/actionwebservice/lib/action_web_service/protocol.rb @@ -1,4 +1,4 @@ require 'action_web_service/protocol/abstract' require 'action_web_service/protocol/registry' -require 'action_web_service/protocol/soap' -require 'action_web_service/protocol/xmlrpc' +require 'action_web_service/protocol/soap_protocol' +require 'action_web_service/protocol/xmlrpc_protocol' diff --git a/actionwebservice/lib/action_web_service/protocol/soap.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb index 3c527fea93..3c527fea93 100644 --- a/actionwebservice/lib/action_web_service/protocol/soap.rb +++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb diff --git a/actionwebservice/lib/action_web_service/protocol/xmlrpc.rb b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb index 414bcfdbf7..1addccba56 100644 --- a/actionwebservice/lib/action_web_service/protocol/xmlrpc.rb +++ b/actionwebservice/lib/action_web_service/protocol/xmlrpc_protocol.rb @@ -54,7 +54,6 @@ module ActionWebService # :nodoc: def initialize(container_class) super(container_class) - container_class.write_inheritable_hash('default_system_methods', XmlRpcProtocol => method(:xmlrpc_default_system_handler)) end def unmarshal_request(protocol_request) @@ -153,20 +152,6 @@ module ActionWebService # :nodoc: end private - def xmlrpc_default_system_handler(name, service_class, *args) - case name - when 'system.listMethods' - methods = [] - api = service_class.web_service_api - api.api_methods.each do |name, info| - methods << api.public_api_method_name(name) - end - methods.sort - else - throw :try_default - end - end - def check_array_types(signature) signature.map{|x| x.is_a?(Array) ? Array : x} end diff --git a/actionwebservice/lib/action_web_service/router.rb b/actionwebservice/lib/action_web_service/router.rb deleted file mode 100644 index 4bfb0bc8b7..0000000000 --- a/actionwebservice/lib/action_web_service/router.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'action_web_service/router/action_controller' -require 'action_web_service/router/wsdl' diff --git a/actionwebservice/lib/action_web_service/router/action_controller.rb b/actionwebservice/lib/action_web_service/router/action_controller.rb deleted file mode 100644 index 591fe4e232..0000000000 --- a/actionwebservice/lib/action_web_service/router/action_controller.rb +++ /dev/null @@ -1,99 +0,0 @@ -module ActionWebService # :nodoc: - module Router # :nodoc: - module ActionController # :nodoc: - def self.append_features(base) # :nodoc: - base.add_web_service_api_callback do |container_class, api| - if container_class.web_service_dispatching_mode == :direct - container_class.class_eval <<-EOS - def api - process_action_service_request - end - EOS - end - end - base.add_web_service_definition_callback do |klass, name, info| - if klass.web_service_dispatching_mode == :delegated - klass.class_eval <<-EOS - def #{name} - process_action_service_request - end - EOS - end - end - base.send(:include, ActionWebService::Router::ActionController::InstanceMethods) - end - - module InstanceMethods # :nodoc: - private - def process_action_service_request - protocol_request = nil - begin - begin - protocol_request = probe_request_protocol(self.request) - rescue Exception => e - unless logger.nil? - logger.error "Invalid request: #{e.message}" - logger.error self.request.raw_post - end - raise - end - if protocol_request - log_request(protocol_request) - protocol_response = dispatch_web_service_request(protocol_request) - log_response(protocol_response) - response_options = { - :type => protocol_response.content_type, - :disposition => 'inline' - } - send_data(protocol_response.raw_body, response_options) - else - logger.fatal "Invalid Action Web Service service or method requested" unless logger.nil? - render_text 'Internal protocol error', "500 Invalid service/method" - end - rescue Exception => e - log_error e unless logger.nil? - exc_response = nil - case web_service_dispatching_mode - when :direct - if self.class.web_service_exception_reporting - exc_response = protocol_request.protocol.marshal_exception(e) - end - when :delegated - web_service = web_service_object(protocol_request.service_name) rescue nil - if web_service && web_service.class.web_service_exception_reporting - exc_response = protocol_request.protocol.marshal_exception(e) rescue nil - end - end - if exc_response - response_options = { - :type => exc_response.content_type, - :disposition => 'inline' - } - log_response exc_response - send_data(exc_response.raw_body, response_options) - else - render_text 'Internal protocol error', "500 #{e.message}" - end - end - end - - def log_request(protocol_request) - unless logger.nil? - web_service_name = protocol_request.web_service_name - method_name = protocol_request.public_method_name - logger.info "\nProcessing Action Web Service Request: #{web_service_name}##{method_name}" - logger.info "Raw Request Body:" - logger.info protocol_request.raw_body - end - end - - def log_response(protocol_response) - unless logger.nil? - logger.info "\nRaw Response Body:" - logger.info protocol_response.raw_body - end - end - end - end - end -end diff --git a/actionwebservice/test/abstract_client.rb b/actionwebservice/test/abstract_client.rb index 0197b87d63..b443fa8ffb 100644 --- a/actionwebservice/test/abstract_client.rb +++ b/actionwebservice/test/abstract_client.rb @@ -71,7 +71,7 @@ module ClientTest end def dispatch_request(protocol_request) - dispatch_web_service_request(protocol_request) + dispatch_protocol_request(protocol_request) end end diff --git a/actionwebservice/test/router_action_controller_test.rb b/actionwebservice/test/dispatcher_action_controller_test.rb index 4f6d40e738..11ab21c6a6 100644 --- a/actionwebservice/test/router_action_controller_test.rb +++ b/actionwebservice/test/dispatcher_action_controller_test.rb @@ -1,11 +1,18 @@ require File.dirname(__FILE__) + '/abstract_soap' require 'wsdl/parser' -module RouterActionControllerTest +module DispatcherActionControllerTest class API < ActionWebService::API::Base api_method :add, :expects => [:int, :int], :returns => [:int] end + class DirectAPI < ActionWebService::API::Base + api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] + api_method :before_filtered + api_method :after_filtered, :returns => [:int] + api_method :thrower + end + class Service < ActionWebService::Base web_service_api API @@ -15,21 +22,20 @@ module RouterActionControllerTest @added = a + b end end - - class DelegatedController < ActionController::Base + + class AbstractController < ActionController::Base + def generate_wsdl(container, uri, soap_action_base) + to_wsdl(container, uri, soap_action_base) + end + end + + class DelegatedController < AbstractController web_service_dispatching_mode :delegated web_service(:test_service) { @service ||= Service.new; @service } end - - class DirectAPI < ActionWebService::API::Base - api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] - api_method :before_filtered - api_method :after_filtered, :returns => [:int] - api_method :thrower - end - - class DirectController < ActionController::Base + + class DirectController < AbstractController web_service_api DirectAPI web_service_dispatching_mode :direct @@ -78,20 +84,22 @@ module RouterActionControllerTest end end -class TC_RouterActionController < AbstractSoapTest - def test_direct_routing - @container = RouterActionControllerTest::DirectController.new +class TC_DispatcherActionController < AbstractSoapTest + include DispatcherActionControllerTest + + def test_direct_dispatching + @container = DirectController.new assert(do_soap_call('Add', 20, 50) == 70) assert(@container.added == 70) end def test_direct_entrypoint - @container = RouterActionControllerTest::DirectController.new + @container = DirectController.new assert(@container.respond_to?(:api)) end def test_direct_filtering - @container = RouterActionControllerTest::DirectController.new + @container = DirectController.new assert(@container.before_filter_called == false) assert(@container.before_filter_target_called == false) assert(do_soap_call('BeforeFiltered').nil?) @@ -104,14 +112,14 @@ class TC_RouterActionController < AbstractSoapTest assert(@container.after_filter_target_called == true) end - def test_delegated_routing - @container = RouterActionControllerTest::DelegatedController.new + def test_delegated_dispatching + @container = DelegatedController.new assert(do_soap_call('Add', 50, 80) == 130) assert(service.added == 130) end def test_exception_marshaling - @container = RouterActionControllerTest::DirectController.new + @container = DirectController.new result = do_soap_call('Thrower') exception = result.detail assert(exception.cause.is_a?(RuntimeError)) @@ -122,9 +130,21 @@ class TC_RouterActionController < AbstractSoapTest end end + def test_wsdl_generation + ensure_valid_wsdl_generation DelegatedController.new + ensure_valid_wsdl_generation DirectController.new + end + + def + + def test_wsdl_action + ensure_valid_wsdl_action DelegatedController.new + ensure_valid_wsdl_action DirectController.new + end + protected def service_name - @container.is_a?(RouterActionControllerTest::DelegatedController) ? 'test_service' : 'api' + @container.is_a?(DelegatedController) ? 'test_service' : 'api' end def service @@ -136,4 +156,31 @@ class TC_RouterActionController < AbstractSoapTest response = @container.process(test_request, test_response) end end + + def ensure_valid_wsdl_generation(controller) + wsdl = controller.generate_wsdl(controller, 'http://localhost:3000/test/', '/test') + ensure_valid_wsdl(wsdl) + end + + def ensure_valid_wsdl(wsdl) + definitions = WSDL::Parser.new.parse(wsdl) + assert(definitions.is_a?(WSDL::Definitions)) + definitions.bindings.each do |binding| + assert(binding.name.name.index(':').nil?) + end + definitions.services.each do |service| + service.ports.each do |port| + assert(port.name.name.index(':').nil?) + end + end + end + + def ensure_valid_wsdl_action(controller) + test_request = ActionController::TestRequest.new({ 'action' => 'wsdl' }) + test_request.env['REQUEST_METHOD'] = 'GET' + test_request.env['HTTP_HOST'] = 'localhost:3000' + test_response = ActionController::TestResponse.new + wsdl = controller.process(test_request, test_response).body + ensure_valid_wsdl(wsdl) + end end diff --git a/actionwebservice/test/invocation_test.rb b/actionwebservice/test/invocation_test.rb index 8ca80ec8f2..0d519bf770 100644 --- a/actionwebservice/test/invocation_test.rb +++ b/actionwebservice/test/invocation_test.rb @@ -58,9 +58,6 @@ module InvocationTest def only_two end - def not_public - end - protected def intercept_before(name, args) @before_invoked = name @@ -90,10 +87,7 @@ class TC_Invocation < Test::Unit::TestCase def test_invocation assert(perform_invocation(:add, 5, 10) == 15) assert(perform_invocation(:transmogrify, "hello") == "HELLO") - assert_raises(InvocationError) do - perform_invocation(:not_public) - end - assert_raises(InvocationError) do + assert_raises(NoMethodError) do perform_invocation(:nonexistent_method_xyzzy) end end @@ -150,9 +144,6 @@ class TC_Invocation < Test::Unit::TestCase private def perform_invocation(method_name, *args, &block) - public_method_name = @service.class.web_service_api.public_api_method_name(method_name) - args ||= [] - request = InvocationRequest.new(ConcreteInvocation, public_method_name, method_name, args) - @service.perform_invocation(request, &block) + @service.perform_invocation(method_name, args, &block) end end diff --git a/actionwebservice/test/protocol_soap_test.rb b/actionwebservice/test/protocol_soap_test.rb index 1130dff3a7..c55b7f55af 100644 --- a/actionwebservice/test/protocol_soap_test.rb +++ b/actionwebservice/test/protocol_soap_test.rb @@ -12,6 +12,13 @@ module ProtocolSoapTest end end + class EmptyAPI < ActionWebService::API::Base + end + + class EmptyService < ActionWebService::Base + web_service_api EmptyAPI + end + class API < ActionWebService::API::Base api_method :argument_passing, :expects => [{:int=>:int}, {:string=>:string}, {:array=>[:int]}], :returns => [:bool] api_method :array_returner, :returns => [[:int]] @@ -72,26 +79,19 @@ module ProtocolSoapTest end end - class AbstractContainer - include ActionWebService::API - include ActionWebService::Container - include ActionWebService::Protocol::Registry - include ActionWebService::Protocol::Soap - + class AbstractContainer < ActionController::Base wsdl_service_name 'Test' - def protocol_request(request) - probe_request_protocol(request) - end - - def dispatch_request(protocol_request) - dispatch_web_service_request(protocol_request) + def dispatch_request(request) + protocol_request = probe_request_protocol(request) + dispatch_protocol_request(protocol_request) end end class DelegatedContainer < AbstractContainer web_service_dispatching_mode :delegated web_service :protocol_soap_service, Service.new + web_service :empty_service, EmptyService.new end class DirectContainer < AbstractContainer @@ -144,12 +144,18 @@ module ProtocolSoapTest nil end end + + class EmptyContainer < AbstractContainer + web_service_dispatching_mode :delegated + web_service :empty_service, EmptyService.new + end end class TC_ProtocolSoap < AbstractSoapTest def setup @delegated_container = ProtocolSoapTest::DelegatedContainer.new @direct_container = ProtocolSoapTest::DirectContainer.new + @empty_container = ProtocolSoapTest::EmptyContainer.new end def test_argument_passing @@ -180,6 +186,13 @@ class TC_ProtocolSoap < AbstractSoapTest end end + def test_nonexistent_method + @container = @empty_container + assert_raises(ActionWebService::Dispatcher::DispatcherError) do + do_soap_call('NonexistentMethod') + end + end + def test_exception_thrower in_all_containers do assert_raises(RuntimeError) do @@ -203,15 +216,29 @@ class TC_ProtocolSoap < AbstractSoapTest protected def service_name - @container == @direct_container ? 'api' : 'protocol_soap_service' + case + when @container == @direct_container + 'api' + when @container == @delegated_container + 'protocol_soap_service' + when @container == @empty_container + 'empty_service' + end end def service - @container == @direct_container ? @container : @container.web_service_object(:protocol_soap_service) + case + when @container == @direct_container + @container + when @container == @delegated_container + @container.web_service_object(:protocol_soap_service) + when @container == @empty_container + @container.web_service_object(:empty_service) + end end def in_all_containers(&block) - [@direct_container].each do |container| + [@direct_container, @delegated_container].each do |container| @container = container block.call end @@ -219,8 +246,7 @@ class TC_ProtocolSoap < AbstractSoapTest def do_soap_call(public_method_name, *args) super(public_method_name, *args) do |test_request, test_response| - protocol_request = @container.protocol_request(test_request) - @container.dispatch_request(protocol_request) + @container.dispatch_request(test_request) end end end diff --git a/actionwebservice/test/protocol_xmlrpc_test.rb b/actionwebservice/test/protocol_xmlrpc_test.rb index 7dc5cf0252..cda0bba6d3 100644 --- a/actionwebservice/test/protocol_xmlrpc_test.rb +++ b/actionwebservice/test/protocol_xmlrpc_test.rb @@ -79,18 +79,13 @@ module ProtocolXmlRpcTest $service = Service.new - class Container - include ActionWebService::Container - include ActionWebService::Protocol::Registry - include ActionWebService::Protocol::Soap - include ActionWebService::Protocol::XmlRpc - + class Container < ActionController::Base def protocol_request(request) probe_request_protocol(request) end def dispatch_request(protocol_request) - dispatch_web_service_request(protocol_request) + dispatch_protocol_request(protocol_request) end web_service :xmlrpc, $service @@ -136,11 +131,6 @@ class TC_ProtocolXmlRpc < Test::Unit::TestCase assert($service.default_args == ['test', [1, 2], {'name'=>'value'}]) end - def test_xmlrpc_introspection - retval = do_xmlrpc_call('system.listMethods', 'test', [1, 2], {'name'=>'value'}) - assert(retval == [true, ["Add", "ArrayReturner", "HashReturner", "SomethingHash", "StructArrayReturner"]]) - end - private def do_xmlrpc_call(public_method_name, *args) service_name = 'xmlrpc' diff --git a/actionwebservice/test/router_wsdl_test.rb b/actionwebservice/test/router_wsdl_test.rb deleted file mode 100644 index a441e73f37..0000000000 --- a/actionwebservice/test/router_wsdl_test.rb +++ /dev/null @@ -1,100 +0,0 @@ -require File.dirname(__FILE__) + '/abstract_unit' -require 'wsdl/parser' - -module RouterWsdlTest - class Person < ActionWebService::Struct - member :id, Integer - member :names, [String] - member :lastname, String - member :deleted, TrueClass - end - - class API < ActionWebService::API::Base - api_method :add, :expects => [{:a=>:int}, {:b=>:int}], :returns => [:int] - api_method :find_people, :returns => [[Person]] - api_method :nil_returner - end - - class Service < ActionWebService::Base - web_service_api API - - def add(a, b) - a + b - end - - def find_people - [] - end - - def nil_returner - end - end - - class AbstractController < ActionController::Base - def generate_wsdl(container, uri, soap_action_base) - to_wsdl(container, uri, soap_action_base) - end - end - - class DirectController < AbstractController - web_service_api API - - def add - end - - def find_people - end - - def nil_returner - end - end - - class DelegatedController < AbstractController - web_service_dispatching_mode :delegated - web_service(:test_service) { Service.new } - end -end - -class TC_RouterWsdl < Test::Unit::TestCase - include RouterWsdlTest - - def test_wsdl_generation - ensure_valid_generation DelegatedController.new - ensure_valid_generation DirectController.new - end - - def - - def test_wsdl_action - ensure_valid_wsdl_action DelegatedController.new - ensure_valid_wsdl_action DirectController.new - end - - protected - def ensure_valid_generation(controller) - wsdl = controller.generate_wsdl(controller, 'http://localhost:3000/test/', '/test') - ensure_valid_wsdl(wsdl) - end - - def ensure_valid_wsdl(wsdl) - definitions = WSDL::Parser.new.parse(wsdl) - assert(definitions.is_a?(WSDL::Definitions)) - definitions.bindings.each do |binding| - assert(binding.name.name.index(':').nil?) - end - definitions.services.each do |service| - service.ports.each do |port| - assert(port.name.name.index(':').nil?) - end - end - end - - def ensure_valid_wsdl_action(controller) - test_request = ActionController::TestRequest.new({ 'action' => 'wsdl' }) - test_request.env['REQUEST_METHOD'] = 'GET' - test_request.env['HTTP_HOST'] = 'localhost:3000' - test_response = ActionController::TestResponse.new - wsdl = controller.process(test_request, test_response).body - ensure_valid_wsdl(wsdl) - end -end |