actionwebservice 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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