refactoring:

* move dispatching out of the Container into Dispatcher, it makes more sense
    for Container to only contain the list of web services defined
    in it.

  * collapse Wsdl and ActionController "routers" into
    an ActionController-specific module, no advantage
    to having them seperate as they were quite tightly
    coupled. rename to Dispatcher, to avoi
    confusion with Routing.

  * add a "_thing" suffix to concept-specific filenames. this is so that
    we don't end up with many soap.rb files, for example.

  * remove "virtual invocation" support. adds complexity, and it doesn't
    seem to add any value.


git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@679 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Leon Breedt 2005-02-19 08:29:42 +00:00
parent e7499638d0
commit 418d487020
24 changed files with 404 additions and 551 deletions

@ -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

@ -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.

@ -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" ) }

@ -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

@ -41,7 +41,7 @@
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 @@
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

@ -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'

@ -5,8 +5,6 @@ class ContainerError < ActionWebService::ActionWebServiceError # :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 @@ def web_service_object(web_service_name)
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

@ -0,0 +1,2 @@
require 'action_web_service/dispatcher/abstract'
require 'action_web_service/dispatcher/action_controller_dispatcher'

@ -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

@ -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 @@ def wsdl
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)

@ -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 @@ def self.append_features(base)
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 @@ def method_exempted?(interceptor, method_name)
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

@ -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'

@ -54,7 +54,6 @@ def self.create_protocol_client(api, protocol_name, endpoint_uri, options)
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 @@ def transform_array_types(signature)
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

@ -1,2 +0,0 @@
require 'action_web_service/router/action_controller'
require 'action_web_service/router/wsdl'

@ -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

@ -71,7 +71,7 @@ def protocol_request(request)
end
def dispatch_request(protocol_request)
dispatch_web_service_request(protocol_request)
dispatch_protocol_request(protocol_request)
end
end

@ -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 @@ def add(a, b)
@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 @@ def alwaysok
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 @@ def test_direct_filtering
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 @@ def test_exception_marshaling
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 @@ def do_soap_call(public_method_name, *args)
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

@ -58,9 +58,6 @@ def only_one
def only_two
end
def not_public
end
protected
def intercept_before(name, args)
@before_invoked = name
@ -90,10 +87,7 @@ def setup
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 @@ def test_interception_only_conditions
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

@ -12,6 +12,13 @@ def ==(other)
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 @@ def default(*args)
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 @@ def default
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 @@ def test_struct_array_returner
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 @@ def test_service_name_setting
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 @@ def in_all_containers(&block)
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

@ -79,18 +79,13 @@ def default(*args)
$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 @@ def test_default_api_method
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'

@ -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