Permalink
Browse files

Make all custom types and method calls are declared in the 'urn:Actio…

…nWebService'

namespace as a default, fixes SOAP marshaling for .NET, a regression since the merge.

Make array annotation be recursive in WS::Marshaling::SoapMarshaling, this makes
typed arrays buried in nested structures still be annotated correctly.

Support :layered dispatching mode for XML-RPC namespaced method names.

Change WS::ParamInfo.create signature to require type_binding, and update all
uses of this.

Restore #default_api_method functionality, fixes a regression since the merge.

Fix marshalling of ActiveRecord::Base derivatives, fixes a regression since the merge.

This changeset closes #676, #677, and #678.


git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@811 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 19dddf2 commit 100015cd806e31578a03ba23ffbc12c093118a26 @leonbreedt leonbreedt committed Feb 27, 2005
@@ -1,11 +1,14 @@
*0.6.0* (Unreleased)
- * lib/*, test/*: refactored SOAP and XML-RPC protocol specifics into
- a small seperate library named 'ws', and drop it in vendor. be
- more relaxed about the type of received parameters, perform casting
- for XML-RPC if possible, but fallback to the received parameters.
- performed extensive cleanup of the way we use SOAP, so that marshaling
- of custom and array types should somewhat faster.
+* Improve error message reporting. Bugs in either AWS or the web service itself will send back a protocol-specific error report message if possible, otherwise, provide as much detail as possible.
+
+* Removed type checking of received parameters, and perform casting for XML-RPC if possible, but fallback to the received parameters if casting fails, closes #677
+
+* Refactored SOAP and XML-RPC marshaling and encoding into a small library devoted exclusively to protocol specifics, also cleaned up the SOAP marshaling approach, so that array and custom type marshaling should be a bit faster.
+
+* Add namespaced XML-RPC method name support, closes #678
+
+* Replace '::' with '..' in fully qualified type names for marshaling and WSDL. This improves interoperability with .NET, and closes #676.
*0.5.0* (24th February, 2005)
View
@@ -114,7 +114,7 @@ For this example, protocol requests for +Add+ and +Remove+ methods sent to
=== Delegated dispatching
This mode can be turned on by setting the +web_service_dispatching_mode+ option
-in a controller.
+in a controller to <tt>:delegated</tt>.
In this mode, the controller contains one or more web service objects (objects
that implement an ActionWebService::API::Base definition). These web service
@@ -153,6 +153,50 @@ Other controller actions (actions that aren't the target of a +web_service+ call
are ignored for ActionWebService purposes, and can do normal action tasks.
+=== Layered dispatching
+
+This mode can be turned on by setting the +web_service_dispatching_mode+ option
+in a controller to <tt>:layered</tt>.
+
+This mode is similar to _delegated_ mode, in that multiple web service objects
+can be attached to one controller, however, all protocol requests are sent to a
+single endpoint.
+
+This mode is only usable by XML-RPC. In this mode, method names can contain
+_prefixes_, which will indicate which web service object implements the API
+identified by that prefix.
+
+The _prefix_ can be any word, followed by a period.
+
+==== Layered dispatching example
+
+
+ class ApiController < ApplicationController
+ web_service_dispatching_mode :layered
+
+ web_service :mt, MovableTypeService.new
+ web_service :blogger, BloggerService.new
+ web_service :metaWeblog, MetaWeblogService.new
+ end
+
+ class MovableTypeService < ActionWebService::Base
+ ...
+ end
+
+ class BloggerService < ActionWebService::Base
+ ...
+ end
+
+ class MetaWeblogService < ActionWebService::API::Base
+ ...
+ end
+
+
+For this example, a remote call for a method with a name like
+<tt>mt.getCategories</tt> will be dispatched as the <tt>getCategories</tt>
+method on the <tt>:mt</tt> service.
+
+
== Using the client support
Action Web Service includes client classes that can use the same API
@@ -31,12 +31,14 @@ Rake::RDocTask.new { |rdoc|
rdoc.title = "Action Web Service -- Web services for Action Pack"
rdoc.options << '--line-numbers --inline-source --main README --accessor class_inheritable_option=RW'
rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('CHANGELOG')
rdoc.rdoc_files.include('lib/action_web_service.rb')
rdoc.rdoc_files.include('lib/action_web_service/*.rb')
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/container/*.rb')
rdoc.rdoc_files.include('lib/action_web_service/dispatcher/*.rb')
+ rdoc.rdoc_files.include('lib/action_web_service/protocol/*.rb')
rdoc.rdoc_files.include('lib/action_web_service/support/*.rb')
}
@@ -63,7 +65,7 @@ spec = Gem::Specification.new do |s|
s.require_path = 'lib'
s.autorequire = 'action_web_service'
- s.files = [ "Rakefile", "setup.rb", "README", "TODO", "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" ) }
View
@@ -1,6 +1,3 @@
-= 0.6.0 Tasks
- - finish off tickets #676, #677, #678
-
= Refactoring
- Don't have clean way to go from SOAP Class object to the xsd:NAME type
string -- NaHi possibly looking at remedying this situation
@@ -24,14 +24,16 @@ class Soap < Base
# will be sent with HTTP POST.
#
# Valid options:
- # [<tt>:service_name</tt>] If the remote server has used a custom +wsdl_service_name+
- # option, you must specify it here
+ # [<tt>:type_namespace</tt>] If the remote server has used a custom namespace to
+ # declare its custom types, you can specify it here
+ # [<tt>:method_namespace</tt>] If the remote server has used a custom namespace to
+ # declare its methods, you can specify it here
def initialize(api, endpoint_uri, options={})
super(api, endpoint_uri)
- @service_name = options[:service_name]
- @namespace = @service_name ? '' : "urn:#{@service_name}"
- @marshaler = WS::Marshaling::SoapMarshaler.new
- @encoder = WS::Encoding::SoapRpcEncoding.new
+ @type_namespace = options[:type_namespace] || 'urn:ActionWebService'
+ @method_namespace = options[:method_namespace] || 'urn:ActionWebService'
+ @marshaler = WS::Marshaling::SoapMarshaler.new @type_namespace
+ @encoder = WS::Encoding::SoapRpcEncoding.new @method_namespace
@soap_action_base = options[:soap_action_base]
@soap_action_base ||= URI.parse(endpoint_uri).path
@driver = create_soap_rpc_driver(api, endpoint_uri)
@@ -53,7 +55,7 @@ def create_soap_rpc_driver(api, endpoint_uri)
driver.mapping_registry = @marshaler.registry
api.api_methods.each do |name, info|
public_name = api.public_api_method_name(name)
- qname = XSD::QName.new(@namespace, public_name)
+ qname = XSD::QName.new(@method_namespace, public_name)
action = soap_action(public_name)
expects = info[:expects]
returns = info[:returns]
@@ -56,7 +56,7 @@ def transform_outgoing_method_params(method_name, params)
i = 0
expects.each do |spec|
type_binding = @marshaler.register_type(spec)
- info = WS::ParamInfo.create(spec, i, type_binding)
+ info = WS::ParamInfo.create(spec, type_binding, i)
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
i += 1
end
@@ -68,7 +68,7 @@ def transform_return_value(method_name, return_value)
info = @api.api_methods[method_name.to_sym]
return true unless returns = info[:returns]
type_binding = @marshaler.register_type(returns[0])
- info = WS::ParamInfo.create(returns[0], 0, type_binding)
+ info = WS::ParamInfo.create(returns[0], type_binding, 0)
info.name = 'return'
@marshaler.transform_inbound(WS::Param.new(return_value, info))
end
@@ -19,15 +19,19 @@ def invoke_web_service_request(protocol_request)
case web_service_dispatching_mode
when :direct
web_service_direct_invoke(invocation)
- when :delegated
+ when :delegated, :layered
web_service_delegated_invoke(invocation)
end
end
def web_service_direct_invoke(invocation)
@method_params = invocation.method_ordered_params
return_value = self.__send__(invocation.api_method_name)
- returns = invocation.returns ? invocation.returns[0] : nil
+ if invocation.api.has_api_method?(invocation.api_method_name)
+ returns = invocation.returns ? invocation.returns[0] : nil
+ else
+ returns = return_value.class
+ end
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
end
@@ -44,29 +48,43 @@ def web_service_delegated_invoke(invocation)
end
def web_service_invocation(request)
+ public_method_name = request.method_name
invocation = Invocation.new
invocation.protocol = request.protocol
invocation.service_name = request.service_name
+ if web_service_dispatching_mode == :layered
+ if request.method_name =~ /^([^\.]+)\.(.*)$/
+ public_method_name = $2
+ invocation.service_name = $1
+ end
+ end
+ invocation.public_method_name = public_method_name
case web_service_dispatching_mode
when :direct
invocation.api = self.class.web_service_api
invocation.service = self
- when :delegated
- invocation.service = web_service_object(request.service_name) rescue nil
+ when :delegated, :layered
+ invocation.service = web_service_object(invocation.service_name) rescue nil
unless invocation.service
- raise(DispatcherError, "failed to instantiate service #{invocation.service_name}")
+ raise(DispatcherError, "service #{invocation.service_name} not available")
end
invocation.api = invocation.service.class.web_service_api
end
- public_method_name = request.method_name
- unless invocation.api.has_public_api_method?(public_method_name)
- raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
+ if invocation.api.has_public_api_method?(public_method_name)
+ invocation.api_method_name = invocation.api.api_method_name(public_method_name)
+ else
+ if invocation.api.default_api_method.nil?
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
+ else
+ invocation.api_method_name = invocation.api.default_api_method.to_s.to_sym
+ end
+ end
+ unless invocation.service.respond_to?(invocation.api_method_name)
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method_name})")
end
- invocation.public_method_name = public_method_name
- invocation.api_method_name = invocation.api.api_method_name(public_method_name)
info = invocation.api.api_methods[invocation.api_method_name]
- invocation.expects = info[:expects]
- invocation.returns = info[:returns]
+ invocation.expects = info ? info[:expects] : nil
+ invocation.returns = info ? info[:returns] : nil
if invocation.expects
i = 0
invocation.method_ordered_params = request.method_params.map do |param|
@@ -83,7 +101,7 @@ def web_service_invocation(request)
params = []
invocation.expects.each do |spec|
type_binding = invocation.protocol.register_signature_type(spec)
- info = WS::ParamInfo.create(spec, i, type_binding)
+ info = WS::ParamInfo.create(spec, type_binding, i)
params << WS::Param.new(invocation.method_ordered_params[i], info)
i += 1
end
@@ -96,10 +114,15 @@ def web_service_invocation(request)
invocation.method_ordered_params = []
invocation.method_named_params = {}
end
+ if invocation.returns
+ invocation.returns.each do |spec|
+ invocation.protocol.register_signature_type(spec)
+ end
+ end
invocation
end
- class Invocation
+ class Invocation # :nodoc:
attr_accessor :protocol
attr_accessor :service_name
attr_accessor :api
Oops, something went wrong.

0 comments on commit 100015c

Please sign in to comment.