actionwebservice 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG +21 -0
  2. data/README +50 -6
  3. data/Rakefile +9 -9
  4. data/TODO +6 -6
  5. data/lib/action_web_service.rb +4 -3
  6. data/lib/action_web_service/api.rb +248 -1
  7. data/lib/action_web_service/casting.rb +111 -0
  8. data/lib/action_web_service/client/soap_client.rb +17 -33
  9. data/lib/action_web_service/client/xmlrpc_client.rb +10 -34
  10. data/lib/action_web_service/container/delegated_container.rb +1 -1
  11. data/lib/action_web_service/dispatcher/abstract.rb +52 -72
  12. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +71 -55
  13. data/lib/action_web_service/protocol/abstract.rb +82 -3
  14. data/lib/action_web_service/protocol/discovery.rb +2 -2
  15. data/lib/action_web_service/protocol/soap_protocol.rb +95 -22
  16. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +197 -0
  17. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +56 -24
  18. data/lib/action_web_service/scaffolding.rb +237 -0
  19. data/lib/action_web_service/struct.rb +17 -4
  20. data/lib/action_web_service/support/signature_types.rb +194 -0
  21. data/lib/action_web_service/templates/scaffolds/layout.rhtml +65 -0
  22. data/lib/action_web_service/templates/scaffolds/methods.rhtml +6 -0
  23. data/lib/action_web_service/templates/scaffolds/parameters.rhtml +28 -0
  24. data/lib/action_web_service/templates/scaffolds/result.rhtml +30 -0
  25. data/lib/action_web_service/test_invoke.rb +23 -42
  26. data/test/abstract_dispatcher.rb +102 -48
  27. data/test/abstract_unit.rb +1 -1
  28. data/test/api_test.rb +40 -7
  29. data/test/casting_test.rb +82 -0
  30. data/test/client_soap_test.rb +3 -0
  31. data/test/client_xmlrpc_test.rb +5 -1
  32. data/test/dispatcher_action_controller_soap_test.rb +9 -12
  33. data/test/dispatcher_action_controller_xmlrpc_test.rb +1 -11
  34. data/test/run +1 -0
  35. data/test/scaffolded_controller_test.rb +67 -0
  36. data/test/struct_test.rb +33 -21
  37. data/test/test_invoke_test.rb +1 -1
  38. metadata +18 -31
  39. data/lib/action_web_service/api/base.rb +0 -135
  40. data/lib/action_web_service/vendor/ws.rb +0 -4
  41. data/lib/action_web_service/vendor/ws/common.rb +0 -8
  42. data/lib/action_web_service/vendor/ws/encoding.rb +0 -3
  43. data/lib/action_web_service/vendor/ws/encoding/abstract.rb +0 -26
  44. data/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb +0 -90
  45. data/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb +0 -53
  46. data/lib/action_web_service/vendor/ws/marshaling.rb +0 -3
  47. data/lib/action_web_service/vendor/ws/marshaling/abstract.rb +0 -17
  48. data/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb +0 -277
  49. data/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb +0 -116
  50. data/lib/action_web_service/vendor/ws/types.rb +0 -165
  51. data/test/ws/abstract_encoding.rb +0 -68
  52. data/test/ws/abstract_unit.rb +0 -13
  53. data/test/ws/gencov +0 -3
  54. data/test/ws/run +0 -5
  55. data/test/ws/soap_marshaling_test.rb +0 -91
  56. data/test/ws/soap_rpc_encoding_test.rb +0 -47
  57. data/test/ws/types_test.rb +0 -43
  58. data/test/ws/xmlrpc_encoding_test.rb +0 -34
@@ -0,0 +1,111 @@
1
+ require 'time'
2
+ require 'date'
3
+
4
+ module ActionWebService # :nodoc:
5
+ module Casting # :nodoc:
6
+ class CastingError < ActionWebServiceError # :nodoc:
7
+ end
8
+
9
+ # Performs casting of arbitrary values into the correct types for the signature
10
+ class BaseCaster
11
+ def initialize(api_method)
12
+ @api_method = api_method
13
+ end
14
+
15
+ # Coerces the parameters in +params+ (an Enumerable) into the types
16
+ # this method expects
17
+ def cast_expects(params)
18
+ self.class.cast_expects(@api_method, params)
19
+ end
20
+
21
+ # Coerces the given +return_value+ into the the type returned by this
22
+ # method
23
+ def cast_returns(return_value)
24
+ self.class.cast_returns(@api_method, return_value)
25
+ end
26
+
27
+ class << self
28
+ include ActionWebService::SignatureTypes
29
+
30
+ def cast_expects(api_method, params) # :nodoc:
31
+ return [] if api_method.expects.nil?
32
+ api_method.expects.zip(params).map{ |type, param| cast(param, type) }
33
+ end
34
+
35
+ def cast_returns(api_method, return_value) # :nodoc:
36
+ return nil if api_method.returns.nil?
37
+ cast(return_value, api_method.returns[0])
38
+ end
39
+
40
+ def cast(value, signature_type) # :nodoc:
41
+ return value if signature_type.nil? # signature.length != params.length
42
+ unless signature_type.array? || signature_type.structured?
43
+ return value if canonical_type(value.class) == signature_type.type
44
+ end
45
+ if signature_type.array?
46
+ unless value.respond_to?(:entries) && !value.is_a?(String)
47
+ raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
48
+ end
49
+ value.entries.map do |entry|
50
+ cast(entry, signature_type.element_type)
51
+ end
52
+ elsif signature_type.structured?
53
+ cast_to_structured_type(value, signature_type)
54
+ elsif !signature_type.custom?
55
+ cast_base_type(value, signature_type)
56
+ end
57
+ end
58
+
59
+ def cast_base_type(value, signature_type) # :nodoc:
60
+ case signature_type.type
61
+ when :int
62
+ Integer(value)
63
+ when :string
64
+ value.to_s
65
+ when :bool
66
+ return false if value.nil?
67
+ return value if value == true || value == false
68
+ case value.to_s.downcase
69
+ when '1', 'true', 'y', 'yes'
70
+ true
71
+ when '0', 'false', 'n', 'no'
72
+ false
73
+ else
74
+ raise CastingError, "Don't know how to cast #{value.class} into Boolean"
75
+ end
76
+ when :float
77
+ Float(value)
78
+ when :time
79
+ Time.parse(value.to_s)
80
+ when :date
81
+ Date.parse(value.to_s)
82
+ when :datetime
83
+ DateTime.parse(value.to_s)
84
+ end
85
+ end
86
+
87
+ def cast_to_structured_type(value, signature_type) # :nodoc:
88
+ obj = nil
89
+ obj = value if canonical_type(value.class) == canonical_type(signature_type.type)
90
+ obj ||= signature_type.type_class.new
91
+ if value.respond_to?(:each_pair)
92
+ klass = signature_type.type_class
93
+ value.each_pair do |name, val|
94
+ type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
95
+ val = cast(val, type) if type
96
+ obj.__send__("#{name}=", val) if obj.respond_to?(name)
97
+ end
98
+ elsif value.respond_to?(:attributes)
99
+ signature_type.each_member do |name, type|
100
+ val = value.__send__(name)
101
+ obj.__send__("#{name}=", cast(val, type)) if obj.respond_to?(name)
102
+ end
103
+ else
104
+ raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
105
+ end
106
+ obj
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -46,8 +46,7 @@ module ActionWebService # :nodoc:
46
46
  @type_namespace = options[:type_namespace] || 'urn:ActionWebService'
47
47
  @method_namespace = options[:method_namespace] || 'urn:ActionWebService'
48
48
  @driver_options = options[:driver_options] || {}
49
- @marshaler = WS::Marshaling::SoapMarshaler.new @type_namespace
50
- @encoder = WS::Encoding::SoapRpcEncoding.new @method_namespace
49
+ @protocol = ActionWebService::Protocol::Soap::SoapProtocol.new
51
50
  @soap_action_base = options[:soap_action_base]
52
51
  @soap_action_base ||= URI.parse(endpoint_uri).path
53
52
  @driver = create_soap_rpc_driver(api, endpoint_uri)
@@ -58,7 +57,10 @@ module ActionWebService # :nodoc:
58
57
 
59
58
  protected
60
59
  def perform_invocation(method_name, args)
61
- @driver.send(method_name, *args)
60
+ method = @api.api_methods[method_name.to_sym]
61
+ args = method.cast_expects(args.dup) rescue args
62
+ return_value = @driver.send(method_name, *args)
63
+ method.cast_returns(return_value.dup) rescue return_value
62
64
  end
63
65
 
64
66
  def soap_action(method_name)
@@ -67,48 +69,30 @@ module ActionWebService # :nodoc:
67
69
 
68
70
  private
69
71
  def create_soap_rpc_driver(api, endpoint_uri)
70
- register_api(@marshaler, api)
72
+ @protocol.register_api(api)
71
73
  driver = SoapDriver.new(endpoint_uri, nil)
72
- driver.mapping_registry = @marshaler.registry
73
- api.api_methods.each do |name, info|
74
- public_name = api.public_api_method_name(name)
75
- qname = XSD::QName.new(@method_namespace, public_name)
76
- action = soap_action(public_name)
77
- expects = info[:expects]
78
- returns = info[:returns]
74
+ driver.mapping_registry = @protocol.marshaler.registry
75
+ api.api_methods.each do |name, method|
76
+ qname = XSD::QName.new(@method_namespace, method.public_name)
77
+ action = soap_action(method.public_name)
78
+ expects = method.expects
79
+ returns = method.returns
79
80
  param_def = []
80
- i = 0
81
81
  if expects
82
- expects.each do |spec|
83
- param_name = spec.is_a?(Hash) ? spec.keys[0].to_s : "param#{i}"
84
- type_binding = @marshaler.register_type(spec)
85
- param_def << ['in', param_name, type_binding.mapping]
86
- i += 1
82
+ expects.each do |type|
83
+ type_binding = @protocol.marshaler.lookup_type(type)
84
+ param_def << ['in', type.name, type_binding.mapping]
87
85
  end
88
86
  end
89
87
  if returns
90
- type_binding = @marshaler.register_type(returns[0])
88
+ type_binding = @protocol.marshaler.lookup_type(returns[0])
91
89
  param_def << ['retval', 'return', type_binding.mapping]
92
90
  end
93
- driver.add_method(qname, action, name.to_s, param_def)
91
+ driver.add_method(qname, action, method.name.to_s, param_def)
94
92
  end
95
93
  driver
96
94
  end
97
95
 
98
- def register_api(marshaler, api)
99
- type_bindings = []
100
- api.api_methods.each do |name, info|
101
- expects, returns = info[:expects], info[:returns]
102
- if expects
103
- expects.each{|type| type_bindings << marshaler.register_type(type)}
104
- end
105
- if returns
106
- returns.each{|type| type_bindings << marshaler.register_type(type)}
107
- end
108
- end
109
- type_bindings
110
- end
111
-
112
96
  class SoapDriver < SOAP::RPC::Driver # :nodoc:
113
97
  def add_method(qname, soapaction, name, param_def)
114
98
  @proxy.add_rpc_method(qname, soapaction, name, param_def)
@@ -30,47 +30,23 @@ module ActionWebService # :nodoc:
30
30
  def initialize(api, endpoint_uri, options={})
31
31
  @api = api
32
32
  @handler_name = options[:handler_name]
33
+ @protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
33
34
  @client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
34
- @marshaler = WS::Marshaling::XmlRpcMarshaler.new
35
35
  end
36
36
 
37
37
  protected
38
38
  def perform_invocation(method_name, args)
39
- args = transform_outgoing_method_params(method_name, args)
40
- ok, return_value = @client.call2(public_name(method_name), *args)
41
- return transform_return_value(method_name, return_value) if ok
42
- raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
43
- end
44
-
45
- def transform_outgoing_method_params(method_name, params)
46
- info = @api.api_methods[method_name.to_sym]
47
- expects = info[:expects]
48
- expects_length = expects.nil?? 0 : expects.length
49
- if expects_length != params.length
50
- raise(ClientError, "API declares #{public_name(method_name)} to accept " +
51
- "#{expects_length} parameters, but #{params.length} parameters " +
52
- "were supplied")
39
+ method = @api.api_methods[method_name.to_sym]
40
+ if method.expects && method.expects.length != args.length
41
+ raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
53
42
  end
54
- params = params.dup
55
- if expects_length > 0
56
- i = 0
57
- expects.each do |spec|
58
- type_binding = @marshaler.register_type(spec)
59
- info = WS::ParamInfo.create(spec, type_binding, i)
60
- params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
61
- i += 1
62
- end
43
+ args = method.cast_expects(args.dup) rescue args
44
+ if method.expects
45
+ method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
63
46
  end
64
- params
65
- end
66
-
67
- def transform_return_value(method_name, return_value)
68
- info = @api.api_methods[method_name.to_sym]
69
- return true unless returns = info[:returns]
70
- type_binding = @marshaler.register_type(returns[0])
71
- info = WS::ParamInfo.create(returns[0], type_binding, 0)
72
- info.name = 'return'
73
- @marshaler.transform_inbound(WS::Param.new(return_value, info))
47
+ ok, return_value = @client.call2(public_name(method_name), *args)
48
+ return (method.cast_returns(return_value.dup) rescue return_value) if ok
49
+ raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
74
50
  end
75
51
 
76
52
  def public_name(method_name)
@@ -79,7 +79,7 @@ module ActionWebService # :nodoc:
79
79
  raise(ContainerError, "no such web service '#{web_service_name}'")
80
80
  end
81
81
  service = info[:block]
82
- service ? instance_eval(&service) : info[:object]
82
+ service ? self.instance_eval(&service) : info[:object]
83
83
  end
84
84
  end
85
85
  end
@@ -12,14 +12,6 @@ module ActionWebService # :nodoc:
12
12
  base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
13
13
  end
14
14
 
15
- def self.layered_service_name(public_method_name) # :nodoc:
16
- if public_method_name =~ /^([^\.]+)\.(.*)$/
17
- $1
18
- else
19
- nil
20
- end
21
- end
22
-
23
15
  module InstanceMethods # :nodoc:
24
16
  private
25
17
  def invoke_web_service_request(protocol_request)
@@ -34,30 +26,24 @@ module ActionWebService # :nodoc:
34
26
 
35
27
  def web_service_direct_invoke(invocation)
36
28
  @method_params = invocation.method_ordered_params
37
- arity = method(invocation.api_method_name).arity rescue 0
29
+ arity = method(invocation.api_method.name).arity rescue 0
38
30
  if arity < 0 || arity > 0
39
- return_value = self.__send__(invocation.api_method_name, *@method_params)
31
+ return_value = self.__send__(invocation.api_method.name, *@method_params)
40
32
  else
41
- return_value = self.__send__(invocation.api_method_name)
33
+ return_value = self.__send__(invocation.api_method.name)
42
34
  end
43
- if invocation.api.has_api_method?(invocation.api_method_name)
44
- returns = invocation.returns ? invocation.returns[0] : nil
45
- else
46
- returns = return_value.class
47
- end
48
- invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
35
+ web_service_create_response(invocation.protocol, invocation.api, invocation.api_method, return_value)
49
36
  end
50
37
 
51
38
  def web_service_delegated_invoke(invocation)
52
39
  cancellation_reason = nil
53
- return_value = invocation.service.perform_invocation(invocation.api_method_name, invocation.method_ordered_params) do |x|
40
+ return_value = invocation.service.perform_invocation(invocation.api_method.name, invocation.method_ordered_params) do |x|
54
41
  cancellation_reason = x
55
42
  end
56
43
  if cancellation_reason
57
44
  raise(DispatcherError, "request canceled: #{cancellation_reason}")
58
45
  end
59
- returns = invocation.returns ? invocation.returns[0] : nil
60
- invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
46
+ web_service_create_response(invocation.protocol, invocation.api, invocation.api_method, return_value)
61
47
  end
62
48
 
63
49
  def web_service_invocation(request)
@@ -66,86 +52,80 @@ module ActionWebService # :nodoc:
66
52
  invocation.protocol = request.protocol
67
53
  invocation.service_name = request.service_name
68
54
  if web_service_dispatching_mode == :layered
69
- if request.method_name =~ /^([^\.]+)\.(.*)$/
70
- public_method_name = $2
71
- invocation.service_name = $1
55
+ case invocation.protocol
56
+ when Protocol::Soap::SoapProtocol
57
+ soap_action = request.protocol_options[:soap_action]
58
+ if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
59
+ invocation.service_name = $1
60
+ end
61
+ when Protocol::XmlRpc::XmlRpcProtocol
62
+ if request.method_name =~ /^([^\.]+)\.(.*)$/
63
+ public_method_name = $2
64
+ invocation.service_name = $1
65
+ end
72
66
  end
73
67
  end
74
- invocation.public_method_name = public_method_name
75
68
  case web_service_dispatching_mode
76
69
  when :direct
77
70
  invocation.api = self.class.web_service_api
78
71
  invocation.service = self
79
72
  when :delegated, :layered
80
- invocation.service = web_service_object(invocation.service_name) rescue nil
81
- unless invocation.service
82
- raise(DispatcherError, "service #{invocation.service_name} not available")
83
- end
73
+ invocation.service = web_service_object(invocation.service_name)
84
74
  invocation.api = invocation.service.class.web_service_api
85
75
  end
76
+ if invocation.api.nil?
77
+ raise(DispatcherError, "no API attached to #{invocation.service.class}")
78
+ end
79
+ invocation.protocol.register_api(invocation.api)
80
+ request.api = invocation.api
86
81
  if invocation.api.has_public_api_method?(public_method_name)
87
- invocation.api_method_name = invocation.api.api_method_name(public_method_name)
82
+ invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
88
83
  else
89
84
  if invocation.api.default_api_method.nil?
90
85
  raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
91
86
  else
92
- invocation.api_method_name = invocation.api.default_api_method.to_s.to_sym
87
+ invocation.api_method = invocation.api.default_api_method_instance
93
88
  end
94
89
  end
95
- unless invocation.service.respond_to?(invocation.api_method_name)
96
- raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method_name})")
90
+ if invocation.service.nil?
91
+ raise(DispatcherError, "no service available for service name #{invocation.service_name}")
97
92
  end
98
- info = invocation.api.api_methods[invocation.api_method_name]
99
- invocation.expects = info ? info[:expects] : nil
100
- invocation.returns = info ? info[:returns] : nil
101
- if invocation.expects
102
- i = 0
103
- invocation.method_ordered_params = request.method_params.map do |param|
104
- if invocation.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
105
- marshaler = invocation.protocol.marshaler
106
- decoded_param = WS::Encoding::XmlRpcDecodedParam.new(param.info.name, param.value)
107
- marshaled_param = marshaler.typed_unmarshal(decoded_param, invocation.expects[i]) rescue nil
108
- param = marshaled_param ? marshaled_param : param
109
- end
110
- i += 1
111
- param.value
112
- end
113
- i = 0
114
- params = []
115
- invocation.expects.each do |spec|
116
- type_binding = invocation.protocol.register_signature_type(spec)
117
- info = WS::ParamInfo.create(spec, type_binding, i)
118
- params << WS::Param.new(invocation.method_ordered_params[i], info)
119
- i += 1
120
- end
121
- invocation.method_ws_params = params
122
- invocation.method_named_params = {}
123
- invocation.method_ws_params.each do |param|
124
- invocation.method_named_params[param.info.name] = param.value
125
- end
126
- else
127
- invocation.method_ordered_params = []
128
- invocation.method_named_params = {}
93
+ unless invocation.service.respond_to?(invocation.api_method.name)
94
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
129
95
  end
130
- if invocation.returns
131
- invocation.returns.each do |spec|
132
- invocation.protocol.register_signature_type(spec)
133
- end
96
+ request.api_method = invocation.api_method
97
+ begin
98
+ invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
99
+ rescue
100
+ logger.warn "Casting of method parameters failed" unless logger.nil?
101
+ invocation.method_ordered_params = request.method_params
102
+ end
103
+ request.method_params = invocation.method_ordered_params
104
+ invocation.method_named_params = {}
105
+ invocation.api_method.param_names.inject(0) do |m, n|
106
+ invocation.method_named_params[n] = invocation.method_ordered_params[m]
107
+ m + 1
134
108
  end
135
109
  invocation
136
110
  end
137
111
 
112
+ def web_service_create_response(protocol, api, api_method, return_value)
113
+ if api.has_api_method?(api_method.name)
114
+ return_type = api_method.returns ? api_method.returns[0] : nil
115
+ return_value = api_method.cast_returns(return_value)
116
+ else
117
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
118
+ end
119
+ protocol.encode_response(api_method.public_name + 'Response', return_value, return_type)
120
+ end
121
+
138
122
  class Invocation # :nodoc:
139
123
  attr_accessor :protocol
140
124
  attr_accessor :service_name
141
125
  attr_accessor :api
142
- attr_accessor :public_method_name
143
- attr_accessor :api_method_name
126
+ attr_accessor :api_method
144
127
  attr_accessor :method_ordered_params
145
128
  attr_accessor :method_named_params
146
- attr_accessor :method_ws_params
147
- attr_accessor :expects
148
- attr_accessor :returns
149
129
  attr_accessor :service
150
130
  end
151
131
  end
@@ -38,9 +38,13 @@ module ActionWebService # :nodoc:
38
38
  module InstanceMethods # :nodoc:
39
39
  private
40
40
  def dispatch_web_service_request
41
- request = discover_web_service_request(@request)
41
+ exception = nil
42
+ begin
43
+ request = discover_web_service_request(@request)
44
+ rescue Exception => e
45
+ exception = e
46
+ end
42
47
  if request
43
- log_request(request, @request.raw_post)
44
48
  response = nil
45
49
  exception = nil
46
50
  bm = Benchmark.measure do
@@ -50,6 +54,7 @@ module ActionWebService # :nodoc:
50
54
  exception = e
51
55
  end
52
56
  end
57
+ log_request(request, @request.raw_post)
53
58
  if exception
54
59
  log_error(exception) unless logger.nil?
55
60
  send_web_service_error_response(request, exception)
@@ -57,7 +62,8 @@ module ActionWebService # :nodoc:
57
62
  send_web_service_response(response, bm.real)
58
63
  end
59
64
  else
60
- exception = DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
65
+ exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
66
+ log_error(exception) unless logger.nil?
61
67
  send_web_service_error_response(request, exception)
62
68
  end
63
69
  rescue Exception => e
@@ -76,15 +82,20 @@ module ActionWebService # :nodoc:
76
82
  unless self.class.web_service_exception_reporting
77
83
  exception = DispatcherError.new("Internal server error (exception raised)")
78
84
  end
79
- response = request.protocol.marshal_response(request.method_name, exception, exception.class)
85
+ api_method = request.api_method
86
+ public_method_name = api_method ? api_method.public_name : request.method_name
87
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
88
+ response = request.protocol.encode_response(public_method_name + 'Response', exception, return_type)
80
89
  send_web_service_response(response)
81
90
  else
82
91
  if self.class.web_service_exception_reporting
83
92
  message = exception.message
93
+ backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
84
94
  else
85
95
  message = "Exception raised"
96
+ backtrace = ""
86
97
  end
87
- render_text("Internal protocol error: #{message}", "500 #{message}")
98
+ render_text("Internal protocol error: #{message}#{backtrace}", "500 Internal Protocol Error")
88
99
  end
89
100
  end
90
101
 
@@ -95,7 +106,7 @@ module ActionWebService # :nodoc:
95
106
  end
96
107
  @session ||= {}
97
108
  @assigns ||= {}
98
- @params['action'] = invocation.api_method_name.to_s
109
+ @params['action'] = invocation.api_method.name.to_s
99
110
  if before_action == false
100
111
  raise(DispatcherError, "Method filtered")
101
112
  end
@@ -107,7 +118,13 @@ module ActionWebService # :nodoc:
107
118
  def log_request(request, body)
108
119
  unless logger.nil?
109
120
  name = request.method_name
110
- params = request.method_params.map{|x| "#{x.info.name}=>#{x.value.inspect}"}
121
+ api_method = request.api_method
122
+ params = request.method_params
123
+ if api_method && api_method.expects
124
+ params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
125
+ else
126
+ params = params.map{ |param| param.inspect }
127
+ end
111
128
  service = request.service_name
112
129
  logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
113
130
  logger.debug(indent(body))
@@ -116,7 +133,8 @@ module ActionWebService # :nodoc:
116
133
 
117
134
  def log_response(response, elapsed=nil)
118
135
  unless logger.nil?
119
- logger.debug("\nWeb Service Response" + (elapsed ? " (%f):" % elapsed : ":"))
136
+ elapsed = (elapsed ? " (%f):" % elapsed : ":")
137
+ logger.debug("\nWeb Service Response" + elapsed + " => #{response.return_value.inspect}")
120
138
  logger.debug(indent(response.body))
121
139
  end
122
140
  end
@@ -160,14 +178,14 @@ module ActionWebService # :nodoc:
160
178
  namespace = 'urn:ActionWebService'
161
179
  soap_action_base = "/#{controller_name}"
162
180
 
163
- marshaler = WS::Marshaling::SoapMarshaler.new(namespace)
181
+ marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
164
182
  apis = {}
165
183
  case dispatching_mode
166
184
  when :direct
167
185
  api = self.class.web_service_api
168
186
  web_service_name = controller_class_name.sub(/Controller$/, '').underscore
169
187
  apis[web_service_name] = [api, register_api(api, marshaler)]
170
- when :delegated
188
+ when :delegated, :layered
171
189
  self.class.web_services.each do |web_service_name, info|
172
190
  service = web_service_object(web_service_name)
173
191
  api = service.class.web_service_api
@@ -197,7 +215,7 @@ module ActionWebService # :nodoc:
197
215
  xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
198
216
  custom_types.each do |binding|
199
217
  case
200
- when binding.is_typed_array?
218
+ when binding.type.array?
201
219
  xm.xsd(:complexType, 'name' => binding.type_name) do
202
220
  xm.xsd(:complexContent) do
203
221
  xm.xsd(:restriction, 'base' => 'soapenc:Array') do
@@ -206,11 +224,11 @@ module ActionWebService # :nodoc:
206
224
  end
207
225
  end
208
226
  end
209
- when binding.is_typed_struct?
227
+ when binding.type.structured?
210
228
  xm.xsd(:complexType, 'name' => binding.type_name) do
211
229
  xm.xsd(:all) do
212
- binding.each_member do |name, spec|
213
- b = marshaler.register_type(spec)
230
+ binding.type.each_member do |name, type|
231
+ b = marshaler.register_type(type)
214
232
  xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
215
233
  end
216
234
  end
@@ -224,34 +242,26 @@ module ActionWebService # :nodoc:
224
242
  # APIs
225
243
  apis.each do |api_name, values|
226
244
  api = values[0]
227
- api.api_methods.each do |name, info|
245
+ api.api_methods.each do |name, method|
228
246
  gen = lambda do |msg_name, direction|
229
- xm.message('name' => msg_name) do
247
+ xm.message('name' => message_name_for(api_name, msg_name)) do
230
248
  sym = nil
231
249
  if direction == :out
232
- returns = info[:returns]
250
+ returns = method.returns
233
251
  if returns
234
252
  binding = marshaler.register_type(returns[0])
235
253
  xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
236
254
  end
237
255
  else
238
- expects = info[:expects]
239
- i = 1
256
+ expects = method.expects
240
257
  expects.each do |type|
241
- if type.is_a?(Hash)
242
- param_name = type.keys.shift
243
- type = type.values.shift
244
- else
245
- param_name = "param#{i}"
246
- end
247
258
  binding = marshaler.register_type(type)
248
- xm.part('name' => param_name, 'type' => binding.qualified_type_name('typens'))
249
- i += 1
259
+ xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
250
260
  end if expects
251
261
  end
252
262
  end
253
263
  end
254
- public_name = api.public_api_method_name(name)
264
+ public_name = method.public_name
255
265
  gen.call(public_name, :in)
256
266
  gen.call("#{public_name}Response", :out)
257
267
  end
@@ -259,11 +269,10 @@ module ActionWebService # :nodoc:
259
269
  # Port
260
270
  port_name = port_name_for(global_service_name, api_name)
261
271
  xm.portType('name' => port_name) do
262
- api.api_methods.each do |name, info|
263
- public_name = api.public_api_method_name(name)
264
- xm.operation('name' => public_name) do
265
- xm.input('message' => "typens:#{public_name}")
266
- xm.output('message' => "typens:#{public_name}Response")
272
+ api.api_methods.each do |name, method|
273
+ xm.operation('name' => method.public_name) do
274
+ xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
275
+ xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
267
276
  end
268
277
  end
269
278
  end
@@ -272,16 +281,15 @@ module ActionWebService # :nodoc:
272
281
  binding_name = binding_name_for(global_service_name, api_name)
273
282
  xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
274
283
  xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
275
- api.api_methods.each do |name, info|
276
- public_name = api.public_api_method_name(name)
277
- xm.operation('name' => public_name) do
284
+ api.api_methods.each do |name, method|
285
+ xm.operation('name' => method.public_name) do
278
286
  case web_service_dispatching_mode
279
- when :direct, :layered
280
- soap_action = soap_action_base + "/api/" + public_name
281
- when :delegated
287
+ when :direct
288
+ soap_action = soap_action_base + "/api/" + method.public_name
289
+ when :delegated, :layered
282
290
  soap_action = soap_action_base \
283
291
  + "/" + api_name.to_s \
284
- + "/" + public_name
292
+ + "/" + method.public_name
285
293
  end
286
294
  xm.soap(:operation, 'soapAction' => soap_action)
287
295
  xm.input do
@@ -307,7 +315,7 @@ module ActionWebService # :nodoc:
307
315
  port_name = port_name_for(global_service_name, api_name)
308
316
  binding_name = binding_name_for(global_service_name, api_name)
309
317
  case web_service_dispatching_mode
310
- when :direct
318
+ when :direct, :layered
311
319
  binding_target = 'api'
312
320
  when :delegated
313
321
  binding_target = api_name.to_s
@@ -328,32 +336,40 @@ module ActionWebService # :nodoc:
328
336
  "#{global_service}#{service.to_s.camelize}Binding"
329
337
  end
330
338
 
339
+ def message_name_for(api_name, message_name)
340
+ mode = web_service_dispatching_mode
341
+ if mode == :layered || mode == :delegated
342
+ api_name.to_s + '-' + message_name
343
+ else
344
+ message_name
345
+ end
346
+ end
347
+
331
348
  def register_api(api, marshaler)
332
349
  bindings = {}
333
350
  traverse_custom_types(api, marshaler) do |binding|
334
- bindings[binding] = nil unless bindings.has_key?(binding.type_class)
351
+ bindings[binding] = nil unless bindings.has_key?(binding)
352
+ element_binding = binding.element_binding
353
+ bindings[binding.element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
335
354
  end
336
355
  bindings.keys
337
356
  end
338
357
 
339
358
  def traverse_custom_types(api, marshaler, &block)
340
- api.api_methods.each do |name, info|
341
- expects, returns = info[:expects], info[:returns]
342
- expects.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if expects
343
- returns.each{|x| traverse_custom_type_spec(marshaler, x, &block)} if returns
359
+ api.api_methods.each do |name, method|
360
+ expects, returns = method.expects, method.returns
361
+ expects.each{ |type| traverse_type(marshaler, type, &block) if type.custom? } if expects
362
+ returns.each{ |type| traverse_type(marshaler, type, &block) if type.custom? } if returns
344
363
  end
345
364
  end
346
365
 
347
- def traverse_custom_type_spec(marshaler, spec, &block)
348
- binding = marshaler.register_type(spec)
349
- if binding.is_typed_struct?
350
- binding.each_member do |name, member_spec|
351
- traverse_custom_type_spec(marshaler, member_spec, &block)
352
- end
353
- elsif binding.is_typed_array?
354
- traverse_custom_type_spec(marshaler, binding.element_binding.type_class, &block)
366
+ def traverse_type(marshaler, type, &block)
367
+ yield marshaler.register_type(type)
368
+ if type.array?
369
+ yield marshaler.register_type(type.element_type)
370
+ type = type.element_type
355
371
  end
356
- yield binding
372
+ type.each_member{ |name, type| traverse_type(marshaler, type, &block) } if type.structured?
357
373
  end
358
374
  end
359
375
  end