axtro-actionwebservice 2.3.5.1.20101118142125

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 (85) hide show
  1. data/CHANGELOG +335 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +381 -0
  4. data/Rakefile +184 -0
  5. data/TODO +32 -0
  6. data/examples/googlesearch/README +143 -0
  7. data/examples/googlesearch/autoloading/google_search_api.rb +51 -0
  8. data/examples/googlesearch/autoloading/google_search_controller.rb +58 -0
  9. data/examples/googlesearch/delegated/google_search_service.rb +109 -0
  10. data/examples/googlesearch/delegated/search_controller.rb +8 -0
  11. data/examples/googlesearch/direct/google_search_api.rb +51 -0
  12. data/examples/googlesearch/direct/search_controller.rb +59 -0
  13. data/examples/metaWeblog/README +17 -0
  14. data/examples/metaWeblog/apis/blogger_api.rb +61 -0
  15. data/examples/metaWeblog/apis/blogger_service.rb +35 -0
  16. data/examples/metaWeblog/apis/meta_weblog_api.rb +68 -0
  17. data/examples/metaWeblog/apis/meta_weblog_service.rb +49 -0
  18. data/examples/metaWeblog/controllers/xmlrpc_controller.rb +17 -0
  19. data/generators/web_service/USAGE +28 -0
  20. data/generators/web_service/templates/api_definition.rb +6 -0
  21. data/generators/web_service/templates/controller.rb +9 -0
  22. data/generators/web_service/templates/functional_test.rb +20 -0
  23. data/generators/web_service/web_service_generator.rb +30 -0
  24. data/lib/action_web_service.rb +61 -0
  25. data/lib/action_web_service/acts_as_web_service.rb +26 -0
  26. data/lib/action_web_service/api.rb +298 -0
  27. data/lib/action_web_service/base.rb +39 -0
  28. data/lib/action_web_service/casting.rb +160 -0
  29. data/lib/action_web_service/client.rb +4 -0
  30. data/lib/action_web_service/client/base.rb +29 -0
  31. data/lib/action_web_service/client/soap_client.rb +114 -0
  32. data/lib/action_web_service/client/xmlrpc_client.rb +59 -0
  33. data/lib/action_web_service/container.rb +4 -0
  34. data/lib/action_web_service/container/action_controller_container.rb +94 -0
  35. data/lib/action_web_service/container/delegated_container.rb +87 -0
  36. data/lib/action_web_service/container/direct_container.rb +70 -0
  37. data/lib/action_web_service/dispatcher.rb +3 -0
  38. data/lib/action_web_service/dispatcher/abstract.rb +209 -0
  39. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +397 -0
  40. data/lib/action_web_service/invocation.rb +203 -0
  41. data/lib/action_web_service/protocol.rb +5 -0
  42. data/lib/action_web_service/protocol/abstract.rb +113 -0
  43. data/lib/action_web_service/protocol/discovery.rb +38 -0
  44. data/lib/action_web_service/protocol/soap_protocol.rb +177 -0
  45. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +243 -0
  46. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb~ +243 -0
  47. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +124 -0
  48. data/lib/action_web_service/scaffolding.rb +282 -0
  49. data/lib/action_web_service/simple.rb +54 -0
  50. data/lib/action_web_service/string_to_datetime_for_soap.rb +17 -0
  51. data/lib/action_web_service/struct.rb +69 -0
  52. data/lib/action_web_service/support/class_inheritable_options.rb +27 -0
  53. data/lib/action_web_service/support/signature_types.rb +262 -0
  54. data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
  55. data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
  56. data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
  57. data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
  58. data/lib/action_web_service/test_invoke.rb +111 -0
  59. data/lib/action_web_service/version.rb +11 -0
  60. data/lib/action_web_service/version.rb~ +10 -0
  61. data/lib/actionwebservice.rb +2 -0
  62. data/setup.rb +1380 -0
  63. data/test/abstract_client.rb +185 -0
  64. data/test/abstract_dispatcher.rb +550 -0
  65. data/test/abstract_unit.rb +44 -0
  66. data/test/api_test.rb +103 -0
  67. data/test/apis/auto_load_api.rb +4 -0
  68. data/test/apis/broken_auto_load_api.rb +3 -0
  69. data/test/base_test.rb +43 -0
  70. data/test/casting_test.rb +96 -0
  71. data/test/client_soap_test.rb +157 -0
  72. data/test/client_xmlrpc_test.rb +155 -0
  73. data/test/container_test.rb +76 -0
  74. data/test/dispatcher_action_controller_soap_test.rb +147 -0
  75. data/test/dispatcher_action_controller_xmlrpc_test.rb +60 -0
  76. data/test/fixtures/db_definitions/mysql.sql +8 -0
  77. data/test/fixtures/db_definitions/sqlite3.sql +8 -0
  78. data/test/fixtures/users.yml +12 -0
  79. data/test/gencov +3 -0
  80. data/test/invocation_test.rb +187 -0
  81. data/test/run +6 -0
  82. data/test/scaffolded_controller_test.rb +148 -0
  83. data/test/struct_test.rb +85 -0
  84. data/test/test_invoke_test.rb +114 -0
  85. metadata +167 -0
@@ -0,0 +1,87 @@
1
+ # encoding: UTF-8
2
+ module ActionWebService # :nodoc:
3
+ module Container # :nodoc:
4
+ module Delegated # :nodoc:
5
+ class ContainerError < ActionWebServiceError # :nodoc:
6
+ end
7
+
8
+ def self.included(base) # :nodoc:
9
+ base.extend(ClassMethods)
10
+ base.send(:include, ActionWebService::Container::Delegated::InstanceMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+ # Declares a web service that will provide access to the API of the given
15
+ # +object+. +object+ must be an ActionWebService::Base derivative.
16
+ #
17
+ # Web service object creation can either be _immediate_, where the object
18
+ # instance is given at class definition time, or _deferred_, where
19
+ # object instantiation is delayed until request time.
20
+ #
21
+ # ==== Immediate web service object example
22
+ #
23
+ # class ApiController < ApplicationController
24
+ # web_service_dispatching_mode :delegated
25
+ #
26
+ # web_service :person, PersonService.new
27
+ # end
28
+ #
29
+ # For deferred instantiation, a block should be given instead of an
30
+ # object instance. This block will be executed in controller instance
31
+ # context, so it can rely on controller instance variables being present.
32
+ #
33
+ # ==== Deferred web service object example
34
+ #
35
+ # class ApiController < ApplicationController
36
+ # web_service_dispatching_mode :delegated
37
+ #
38
+ # web_service(:person) { PersonService.new(request.env) }
39
+ # end
40
+ def web_service(name, object=nil, &block)
41
+ if (object && block_given?) || (object.nil? && block.nil?)
42
+ raise(ContainerError, "either service, or a block must be given")
43
+ end
44
+ name = name.to_sym
45
+ if block_given?
46
+ info = { name => { :block => block } }
47
+ else
48
+ info = { name => { :object => object } }
49
+ end
50
+ write_inheritable_hash("web_services", info)
51
+ call_web_service_definition_callbacks(self, name, info)
52
+ end
53
+
54
+ # Whether this service contains a service with the given +name+
55
+ def has_web_service?(name)
56
+ web_services.has_key?(name.to_sym)
57
+ end
58
+
59
+ def web_services # :nodoc:
60
+ read_inheritable_attribute("web_services") || {}
61
+ end
62
+
63
+ def add_web_service_definition_callback(&block) # :nodoc:
64
+ write_inheritable_array("web_service_definition_callbacks", [block])
65
+ end
66
+
67
+ private
68
+ def call_web_service_definition_callbacks(container_class, web_service_name, service_info)
69
+ (read_inheritable_attribute("web_service_definition_callbacks") || []).each do |block|
70
+ block.call(container_class, web_service_name, service_info)
71
+ end
72
+ end
73
+ end
74
+
75
+ module InstanceMethods # :nodoc:
76
+ def web_service_object(web_service_name)
77
+ info = self.class.web_services[web_service_name.to_sym]
78
+ unless info
79
+ raise(ContainerError, "no such web service '#{web_service_name}'")
80
+ end
81
+ service = info[:block]
82
+ service ? self.instance_eval(&service) : info[:object]
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: UTF-8
2
+ module ActionWebService # :nodoc:
3
+ module Container # :nodoc:
4
+ module Direct # :nodoc:
5
+ class ContainerError < ActionWebServiceError # :nodoc:
6
+ end
7
+
8
+ def self.included(base) # :nodoc:
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ # Attaches ActionWebService API +definition+ to the calling class.
14
+ #
15
+ # Action Controllers can have a default associated API, removing the need
16
+ # to call this method if you follow the Action Web Service naming conventions.
17
+ #
18
+ # A controller with a class name of GoogleSearchController will
19
+ # implicitly load <tt>app/apis/google_search_api.rb</tt>, and expect the
20
+ # API definition class to be named <tt>GoogleSearchAPI</tt> or
21
+ # <tt>GoogleSearchApi</tt>.
22
+ #
23
+ # ==== Service class example
24
+ #
25
+ # class MyService < ActionWebService::Base
26
+ # web_service_api MyAPI
27
+ # end
28
+ #
29
+ # class MyAPI < ActionWebService::API::Base
30
+ # ...
31
+ # end
32
+ #
33
+ # ==== Controller class example
34
+ #
35
+ # class MyController < ActionController::Base
36
+ # web_service_api MyAPI
37
+ # end
38
+ #
39
+ # class MyAPI < ActionWebService::API::Base
40
+ # ...
41
+ # end
42
+ def web_service_api(definition=nil)
43
+ if definition.nil?
44
+ read_inheritable_attribute("web_service_api")
45
+ else
46
+ if definition.is_a?(Symbol)
47
+ raise(ContainerError, "symbols can only be used for #web_service_api inside of a controller")
48
+ end
49
+ unless definition.respond_to?(:ancestors) && definition.ancestors.include?(ActionWebService::API::Base)
50
+ raise(ContainerError, "#{definition.to_s} is not a valid API definition")
51
+ end
52
+ write_inheritable_attribute("web_service_api", definition)
53
+ call_web_service_api_callbacks(self, definition)
54
+ end
55
+ end
56
+
57
+ def add_web_service_api_callback(&block) # :nodoc:
58
+ write_inheritable_array("web_service_api_callbacks", [block])
59
+ end
60
+
61
+ private
62
+ def call_web_service_api_callbacks(container_class, definition)
63
+ (read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
64
+ block.call(container_class, definition)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ # encoding: UTF-8
2
+ require 'action_web_service/dispatcher/abstract'
3
+ require 'action_web_service/dispatcher/action_controller_dispatcher'
@@ -0,0 +1,209 @@
1
+ # encoding: UTF-8
2
+ require 'benchmark'
3
+
4
+ module ActionWebService # :nodoc:
5
+ module Dispatcher # :nodoc:
6
+ class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc:
7
+ def initialize(*args)
8
+ super
9
+ set_backtrace(caller)
10
+ end
11
+ end
12
+
13
+ def self.included(base) # :nodoc:
14
+ base.class_inheritable_option(:web_service_dispatching_mode, :direct)
15
+ base.class_inheritable_option(:web_service_inflect_type, false)
16
+ base.class_inheritable_option(:web_service_exception_reporting, true)
17
+ base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
18
+ end
19
+
20
+ module InstanceMethods # :nodoc:
21
+ private
22
+ def invoke_web_service_request(protocol_request)
23
+ invocation = web_service_invocation(protocol_request)
24
+ if invocation.is_a?(Array) && protocol_request.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
25
+ xmlrpc_multicall_invoke(invocation)
26
+ else
27
+ web_service_invoke(invocation)
28
+ end
29
+ end
30
+
31
+ def web_service_direct_invoke(invocation)
32
+ @method_params = invocation.method_ordered_params
33
+ arity = method(invocation.api_method.name).arity rescue 0
34
+ if arity < 0 || arity > 0
35
+ params = @method_params
36
+ else
37
+ params = []
38
+ end
39
+ web_service_filtered_invoke(invocation, params)
40
+ end
41
+
42
+ def web_service_delegated_invoke(invocation)
43
+ web_service_filtered_invoke(invocation, invocation.method_ordered_params)
44
+ end
45
+
46
+ def web_service_filtered_invoke(invocation, params)
47
+ cancellation_reason = nil
48
+ return_value = invocation.service.perform_invocation(invocation.api_method.name, params) do |x|
49
+ cancellation_reason = x
50
+ end
51
+ if cancellation_reason
52
+ raise(DispatcherError, "request canceled: #{cancellation_reason}")
53
+ end
54
+ return_value
55
+ end
56
+
57
+ def web_service_invoke(invocation)
58
+ case web_service_dispatching_mode
59
+ when :direct
60
+ return_value = web_service_direct_invoke(invocation)
61
+ when :delegated, :layered
62
+ return_value = web_service_delegated_invoke(invocation)
63
+ end
64
+ web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
65
+ end
66
+
67
+ def xmlrpc_multicall_invoke(invocations)
68
+ responses = []
69
+ invocations.each do |invocation|
70
+ if invocation.is_a?(Hash)
71
+ responses << [invocation, nil]
72
+ next
73
+ end
74
+ begin
75
+ case web_service_dispatching_mode
76
+ when :direct
77
+ return_value = web_service_direct_invoke(invocation)
78
+ when :delegated, :layered
79
+ return_value = web_service_delegated_invoke(invocation)
80
+ end
81
+ api_method = invocation.api_method
82
+ if invocation.api.has_api_method?(api_method.name)
83
+ response_type = (api_method.returns ? api_method.returns[0] : nil)
84
+ return_value = api_method.cast_returns(return_value)
85
+ else
86
+ response_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
87
+ end
88
+ responses << [return_value, response_type]
89
+ rescue Exception => e
90
+ responses << [{ 'faultCode' => 3, 'faultString' => e.message }, nil]
91
+ end
92
+ end
93
+ invocation = invocations[0]
94
+ invocation.protocol.encode_multicall_response(responses, invocation.protocol_options)
95
+ end
96
+
97
+ def web_service_invocation(request, level = 0)
98
+ public_method_name = request.method_name
99
+ invocation = Invocation.new
100
+ invocation.protocol = request.protocol
101
+ invocation.protocol_options = request.protocol_options
102
+ invocation.service_name = request.service_name
103
+ if web_service_dispatching_mode == :layered
104
+ case invocation.protocol
105
+ when Protocol::Soap::SoapProtocol
106
+ soap_action = request.protocol_options[:soap_action]
107
+ if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
108
+ invocation.service_name = $1
109
+ end
110
+ when Protocol::XmlRpc::XmlRpcProtocol
111
+ if request.method_name =~ /^([^\.]+)\.(.*)$/
112
+ public_method_name = $2
113
+ invocation.service_name = $1
114
+ end
115
+ end
116
+ end
117
+ if invocation.protocol.is_a? Protocol::XmlRpc::XmlRpcProtocol
118
+ if public_method_name == 'multicall' && invocation.service_name == 'system'
119
+ if level > 0
120
+ raise(DispatcherError, "Recursive system.multicall invocations not allowed")
121
+ end
122
+ multicall = request.method_params.dup
123
+ unless multicall.is_a?(Array) && multicall[0].is_a?(Array)
124
+ raise(DispatcherError, "Malformed multicall (expected array of Hash elements)")
125
+ end
126
+ multicall = multicall[0]
127
+ return multicall.map do |item|
128
+ raise(DispatcherError, "Multicall elements must be Hash") unless item.is_a?(Hash)
129
+ raise(DispatcherError, "Multicall elements must contain a 'methodName' key") unless item.has_key?('methodName')
130
+ method_name = item['methodName']
131
+ params = item.has_key?('params') ? item['params'] : []
132
+ multicall_request = request.dup
133
+ multicall_request.method_name = method_name
134
+ multicall_request.method_params = params
135
+ begin
136
+ web_service_invocation(multicall_request, level + 1)
137
+ rescue Exception => e
138
+ {'faultCode' => 4, 'faultMessage' => e.message}
139
+ end
140
+ end
141
+ end
142
+ end
143
+ case web_service_dispatching_mode
144
+ when :direct
145
+ invocation.api = self.class.web_service_api
146
+ invocation.service = self
147
+ when :delegated, :layered
148
+ invocation.service = web_service_object(invocation.service_name)
149
+ invocation.api = invocation.service.class.web_service_api
150
+ end
151
+ if invocation.api.nil?
152
+ raise(DispatcherError, "no API attached to #{invocation.service.class}")
153
+ end
154
+ invocation.protocol.register_api(invocation.api)
155
+ request.api = invocation.api
156
+ if invocation.api.has_public_api_method?(public_method_name)
157
+ invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
158
+ else
159
+ if invocation.api.default_api_method.nil?
160
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
161
+ else
162
+ invocation.api_method = invocation.api.default_api_method_instance
163
+ end
164
+ end
165
+ if invocation.service.nil?
166
+ raise(DispatcherError, "no service available for service name #{invocation.service_name}")
167
+ end
168
+ unless invocation.service.respond_to?(invocation.api_method.name)
169
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
170
+ end
171
+ request.api_method = invocation.api_method
172
+ begin
173
+ invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
174
+ rescue
175
+ logger.warn "Casting of method parameters failed" unless logger.nil?
176
+ invocation.method_ordered_params = request.method_params
177
+ end
178
+ request.method_params = invocation.method_ordered_params
179
+ invocation.method_named_params = {}
180
+ invocation.api_method.param_names.inject(0) do |m, n|
181
+ invocation.method_named_params[n] = invocation.method_ordered_params[m]
182
+ m + 1
183
+ end
184
+ invocation
185
+ end
186
+
187
+ def web_service_create_response(protocol, protocol_options, api, api_method, return_value)
188
+ if api.has_api_method?(api_method.name)
189
+ return_type = api_method.returns ? api_method.returns[0] : nil
190
+ return_value = api_method.cast_returns(return_value)
191
+ else
192
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
193
+ end
194
+ protocol.encode_response(api_method.public_name + 'Response', return_value, return_type, protocol_options)
195
+ end
196
+
197
+ class Invocation # :nodoc:
198
+ attr_accessor :protocol
199
+ attr_accessor :protocol_options
200
+ attr_accessor :service_name
201
+ attr_accessor :api
202
+ attr_accessor :api_method
203
+ attr_accessor :method_ordered_params
204
+ attr_accessor :method_named_params
205
+ attr_accessor :service
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,397 @@
1
+ # encoding: UTF-8
2
+ require 'benchmark'
3
+ require 'builder/xmlmarkup'
4
+
5
+ module ActionWebService # :nodoc:
6
+ module Dispatcher # :nodoc:
7
+ module ActionController # :nodoc:
8
+ def self.included(base) # :nodoc:
9
+ class << base
10
+ include ClassMethods
11
+ alias_method_chain :inherited, :action_controller
12
+ end
13
+ base.class_eval do
14
+ alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
15
+ end
16
+ base.add_web_service_api_callback do |klass, api|
17
+ if klass.web_service_dispatching_mode == :direct
18
+ klass.class_eval 'def api; dispatch_web_service_request; end'
19
+ end
20
+ end
21
+ base.add_web_service_definition_callback do |klass, name, info|
22
+ if klass.web_service_dispatching_mode == :delegated
23
+ klass.class_eval "def #{name}; dispatch_web_service_request; end"
24
+ elsif klass.web_service_dispatching_mode == :layered
25
+ klass.class_eval 'def api; dispatch_web_service_request; end'
26
+ end
27
+ end
28
+ base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
29
+ end
30
+
31
+ module ClassMethods # :nodoc:
32
+ def inherited_with_action_controller(child)
33
+ inherited_without_action_controller(child)
34
+ # child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
35
+ end
36
+ end
37
+
38
+ module InstanceMethods # :nodoc:
39
+ private
40
+ def dispatch_web_service_request
41
+ method = request.method.to_s.upcase
42
+ allowed_methods = self.class.web_service_api ? (self.class.web_service_api.allowed_http_methods || []) : [ :post ]
43
+ allowed_methods = allowed_methods.map{|m| m.to_s.upcase }
44
+ if !allowed_methods.include?(method)
45
+ render :text => "#{method} not supported", :status=>500
46
+ return
47
+ end
48
+ exception = nil
49
+ begin
50
+ ws_request = discover_web_service_request(request)
51
+ rescue Exception => e
52
+ exception = e
53
+ end
54
+ if ws_request
55
+ ws_response = nil
56
+ exception = nil
57
+ bm = Benchmark.measure do
58
+ begin
59
+ ws_response = invoke_web_service_request(ws_request)
60
+ rescue Exception => e
61
+ exception = e
62
+ end
63
+ end
64
+ log_request(ws_request, request.raw_post)
65
+ if exception
66
+ log_error(exception) unless logger.nil?
67
+ send_web_service_error_response(ws_request, exception)
68
+ else
69
+ send_web_service_response(ws_response, bm.real)
70
+ end
71
+ else
72
+ exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
73
+ log_error(exception) unless logger.nil?
74
+ send_web_service_error_response(ws_request, exception)
75
+ end
76
+ rescue Exception => e
77
+ log_error(e) unless logger.nil?
78
+ send_web_service_error_response(ws_request, e)
79
+ end
80
+
81
+ def send_web_service_response(ws_response, elapsed=nil)
82
+ log_response(ws_response, elapsed)
83
+ options = { :type => ws_response.content_type, :disposition => 'inline' }
84
+ send_data(ws_response.body, options)
85
+ end
86
+
87
+ def send_web_service_error_response(ws_request, exception)
88
+ if ws_request
89
+ unless self.class.web_service_exception_reporting
90
+ exception = DispatcherError.new("Internal server error (exception raised)")
91
+ end
92
+ api_method = ws_request.api_method
93
+ public_method_name = api_method ? api_method.public_name : ws_request.method_name
94
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
95
+ ws_response = ws_request.protocol.encode_response(public_method_name + 'Response', exception, return_type, ws_request.protocol_options)
96
+ send_web_service_response(ws_response)
97
+ else
98
+ if self.class.web_service_exception_reporting
99
+ message = exception.message
100
+ backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
101
+ else
102
+ message = "Exception raised"
103
+ backtrace = ""
104
+ end
105
+ render :status => 500, :text => "Internal protocol error: #{message}#{backtrace}"
106
+ end
107
+ end
108
+
109
+ def web_service_direct_invoke(invocation)
110
+ invocation.method_named_params.each do |name, value|
111
+ params[name] = value
112
+ end
113
+ web_service_direct_invoke_without_controller(invocation)
114
+ end
115
+
116
+ def log_request(ws_request, body)
117
+ unless logger.nil?
118
+ name = ws_request.method_name
119
+ api_method = ws_request.api_method
120
+ params = ws_request.method_params
121
+ if api_method && api_method.expects
122
+ params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
123
+ else
124
+ params = params.map{ |param| param.inspect }
125
+ end
126
+ service = ws_request.service_name
127
+ logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
128
+ logger.debug(indent(body))
129
+ end
130
+ end
131
+
132
+ def log_response(ws_response, elapsed=nil)
133
+ unless logger.nil?
134
+ elapsed = (elapsed ? " (%f):" % elapsed : ":")
135
+ logger.debug("\nWeb Service Response" + elapsed + " => #{ws_response.return_value.inspect}")
136
+ logger.debug(indent(ws_response.body))
137
+ end
138
+ end
139
+
140
+ def indent(body)
141
+ body.split(/\n/).map{|x| " #{x}"}.join("\n")
142
+ end
143
+ end
144
+
145
+ module WsdlAction # :nodoc:
146
+ XsdNs = 'http://www.w3.org/2001/XMLSchema'
147
+ WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
148
+ SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
149
+ SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/'
150
+ SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http'
151
+
152
+ def wsdl
153
+ case request.method
154
+ when :get
155
+ begin
156
+ options = { :type => 'text/xml', :disposition => 'inline' }
157
+ send_data(to_wsdl, options)
158
+ rescue Exception => e
159
+ log_error(e) unless logger.nil?
160
+ end
161
+ when :post
162
+ render :status => 500, :text => 'POST not supported'
163
+ end
164
+ end
165
+
166
+ private
167
+ def base_uri
168
+ host = request.host_with_port
169
+ relative_url_root = ::ActionController::Base.relative_url_root
170
+ scheme = request.ssl? ? 'https' : 'http'
171
+ '%s://%s%s/%s/' % [scheme, host, relative_url_root, self.class.controller_path]
172
+ end
173
+
174
+ def to_wsdl
175
+ xml = ''
176
+ inflect = web_service_inflect_type
177
+ dispatching_mode = web_service_dispatching_mode
178
+ global_service_name = wsdl_service_name
179
+ namespace = wsdl_namespace || 'urn:ActionWebService'
180
+ soap_action_base = "/#{controller_name}"
181
+
182
+ marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
183
+ apis = {}
184
+ case dispatching_mode
185
+ when :direct
186
+ api = self.class.web_service_api
187
+ web_service_name = controller_class_name.sub(/Controller$/, '').underscore
188
+ apis[web_service_name] = [api, register_api(api, marshaler)]
189
+ when :delegated, :layered
190
+ self.class.web_services.each do |web_service_name, info|
191
+ service = web_service_object(web_service_name)
192
+ api = service.class.web_service_api
193
+ apis[web_service_name] = [api, register_api(api, marshaler)]
194
+ end
195
+ end
196
+ custom_types = []
197
+ apis.values.each do |api, bindings|
198
+ bindings.each do |b|
199
+ custom_types << b unless custom_types.include?(b)
200
+ end
201
+ end
202
+
203
+ xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
204
+ xm.instruct!
205
+ xm.definitions('name' => wsdl_service_name,
206
+ 'targetNamespace' => namespace,
207
+ 'xmlns:typens' => namespace,
208
+ 'xmlns:xsd' => XsdNs,
209
+ 'xmlns:soap' => SoapNs,
210
+ 'xmlns:soapenc' => SoapEncodingNs,
211
+ 'xmlns:wsdl' => WsdlNs,
212
+ 'xmlns' => WsdlNs) do
213
+ # Generate XSD
214
+ if custom_types.size > 0
215
+ xm.types do
216
+ xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
217
+ simple_types, array_types, complex_types = [], [], []
218
+ custom_types.each do |binding|
219
+ case
220
+ when binding.type.simple? then simple_types.push binding
221
+ when binding.type.array? then array_types.push binding
222
+ when binding.type.structured? then complex_types.push binding
223
+ end
224
+ end
225
+ simple_types.each do |binding|
226
+ xm.xsd(:simpleType, 'name' => inflect ? binding.type_name.camelize(:lower) : binding.type_name) do
227
+ xm.xsd(:restriction, 'base' => "xsd:#{binding.type.base}") do
228
+ binding.type.restrictions do |name, value|
229
+ xm.xsd(name.to_sym, 'value' => value)
230
+ end
231
+ end
232
+ end
233
+ end
234
+ array_types.each do |binding|
235
+ xm.xsd(:complexType, 'name' => inflect ? binding.type_name.camelize(:lower) : binding.type_name) do
236
+ xm.xsd(:complexContent) do
237
+ xm.xsd(:restriction, 'base' => 'soapenc:Array') do
238
+ xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
239
+ 'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
240
+ end
241
+ end
242
+ end
243
+ end
244
+ complex_types.each do |binding|
245
+ xm.xsd(:complexType, 'name' => inflect ? binding.type_name.camelize(:lower) : binding.type_name) do
246
+ xm.xsd(:all) do
247
+ binding.type.each_member do |name, type, options|
248
+ options ||= {}
249
+ b = marshaler.register_type(type)
250
+ xm.xsd(:element, {'name' => inflect ? name.to_s.camelize : name, 'type' => b.qualified_type_name('typens')}.merge(options))
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ # APIs
260
+ apis.each do |api_name, values|
261
+ api = values[0]
262
+ api.api_methods.each do |name, method|
263
+ gen = lambda do |msg_name, direction|
264
+ xm.message('name' => message_name_for(api_name, msg_name)) do
265
+ sym = nil
266
+ if direction == :out
267
+ returns = method.returns
268
+ if returns
269
+ binding = marshaler.register_type(returns[0])
270
+ xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
271
+ end
272
+ else
273
+ expects = method.expects
274
+ expects.each do |type|
275
+ binding = marshaler.register_type(type)
276
+ xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
277
+ end if expects
278
+ end
279
+ end
280
+ end
281
+ public_name = method.public_name
282
+ gen.call(public_name, :in)
283
+ gen.call("#{public_name}Response", :out)
284
+ end
285
+
286
+ # Port
287
+ port_name = port_name_for(global_service_name, api_name)
288
+ xm.portType('name' => port_name) do
289
+ api.api_methods.each do |name, method|
290
+ xm.operation('name' => method.public_name) do
291
+ xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
292
+ xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
293
+ end
294
+ end
295
+ end
296
+
297
+ # Bind it
298
+ binding_name = binding_name_for(global_service_name, api_name)
299
+ xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
300
+ xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
301
+ api.api_methods.each do |name, method|
302
+ xm.operation('name' => method.public_name) do
303
+ case web_service_dispatching_mode
304
+ when :direct
305
+ soap_action = soap_action_base + "/api/" + method.public_name
306
+ when :delegated, :layered
307
+ soap_action = soap_action_base \
308
+ + "/" + api_name.to_s \
309
+ + "/" + method.public_name
310
+ end
311
+ xm.soap(:operation, 'soapAction' => soap_action)
312
+ xm.input do
313
+ xm.soap(:body,
314
+ 'use' => 'encoded',
315
+ 'namespace' => namespace,
316
+ 'encodingStyle' => SoapEncodingNs)
317
+ end
318
+ xm.output do
319
+ xm.soap(:body,
320
+ 'use' => 'encoded',
321
+ 'namespace' => namespace,
322
+ 'encodingStyle' => SoapEncodingNs)
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ # Define it
330
+ xm.service('name' => "#{global_service_name}Service") do
331
+ apis.each do |api_name, values|
332
+ port_name = port_name_for(global_service_name, api_name)
333
+ binding_name = binding_name_for(global_service_name, api_name)
334
+ case web_service_dispatching_mode
335
+ when :direct, :layered
336
+ binding_target = 'api'
337
+ when :delegated
338
+ binding_target = api_name.to_s
339
+ end
340
+ xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
341
+ xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
342
+ end
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ def port_name_for(global_service, service)
349
+ "#{global_service}#{service.to_s.camelize}Port"
350
+ end
351
+
352
+ def binding_name_for(global_service, service)
353
+ "#{global_service}#{service.to_s.camelize}Binding"
354
+ end
355
+
356
+ def message_name_for(api_name, message_name)
357
+ mode = web_service_dispatching_mode
358
+ if mode == :layered || mode == :delegated
359
+ api_name.to_s + '-' + message_name
360
+ else
361
+ message_name
362
+ end
363
+ end
364
+
365
+ def register_api(api, marshaler)
366
+ bindings = {}
367
+ traverse_custom_types(api, marshaler, bindings) do |binding|
368
+ bindings[binding] = nil unless bindings.has_key?(binding)
369
+ element_binding = binding.element_binding
370
+ bindings[element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
371
+ end
372
+ bindings.keys
373
+ end
374
+
375
+ def traverse_custom_types(api, marshaler, bindings, &block)
376
+ api.api_methods.each do |name, method|
377
+ expects, returns = method.expects, method.returns
378
+ expects.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if expects
379
+ returns.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if returns
380
+ end
381
+ end
382
+
383
+ def traverse_type(marshaler, type, bindings, &block)
384
+ binding = marshaler.register_type(type)
385
+ return if bindings.has_key?(binding)
386
+ bindings[binding] = nil
387
+ yield binding
388
+ if type.array?
389
+ yield marshaler.register_type(type.element_type)
390
+ type = type.element_type
391
+ end
392
+ type.each_member{ |name, type| traverse_type(marshaler, type, bindings, &block) } if type.structured?
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end