From 418d487020d24e69b528fdbedfecb20a87f99fcb Mon Sep 17 00:00:00 2001 From: Leon Breedt Date: Sat, 19 Feb 2005 08:29:42 +0000 Subject: refactoring: * move dispatching out of the Container into Dispatcher, it makes more sense for Container to only contain the list of web services defined in it. * collapse Wsdl and ActionController "routers" into an ActionController-specific module, no advantage to having them seperate as they were quite tightly coupled. rename to Dispatcher, to avoi confusion with Routing. * add a "_thing" suffix to concept-specific filenames. this is so that we don't end up with many soap.rb files, for example. * remove "virtual invocation" support. adds complexity, and it doesn't seem to add any value. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@679 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../dispatcher/action_controller_dispatcher.rb | 301 +++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb (limited to 'actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb') diff --git a/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb new file mode 100644 index 0000000000..68c0f4ffb3 --- /dev/null +++ b/actionwebservice/lib/action_web_service/dispatcher/action_controller_dispatcher.rb @@ -0,0 +1,301 @@ +module ActionWebService # :nodoc: + module Dispatcher # :nodoc: + module ActionController # :nodoc: + def self.append_features(base) # :nodoc: + super + base.class_eval do + class << self + alias_method :inherited_without_action_controller, :inherited + end + end + base.class_eval do + alias_method :before_direct_invoke_without_action_controller, :before_direct_invoke + alias_method :after_direct_invoke_without_action_controller, :after_direct_invoke + end + base.add_web_service_api_callback do |klass, api| + if klass.web_service_dispatching_mode == :direct + klass.class_eval <<-EOS + def api + controller_dispatch_web_service_request + end + EOS + end + end + base.add_web_service_definition_callback do |klass, name, info| + if klass.web_service_dispatching_mode == :delegated + klass.class_eval <<-EOS + def #{name} + controller_dispatch_web_service_request + end + EOS + end + end + base.extend(ClassMethods) + base.send(:include, ActionWebService::Dispatcher::ActionController::Invocation) + end + + module ClassMethods # :nodoc: + def inherited(child) + inherited_without_action_controller(child) + child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlGeneration) + end + end + + module Invocation # :nodoc: + private + def controller_dispatch_web_service_request + request, response, elapsed, exception = dispatch_web_service_request(@request) + if response + begin + log_request(request) + log_error(exception) if exception && logger + log_response(response, elapsed) + response_options = { :type => response.content_type, :disposition => 'inline' } + send_data(response.raw_body, response_options) + rescue Exception => e + log_error(e) unless logger.nil? + render_text("Internal protocol error", "500 Internal Server Error") + end + else + logger.error("No response available") unless logger.nil? + render_text("Internal protocol error", "500 Internal Server Error") + end + end + + def before_direct_invoke(request) + before_direct_invoke_without_action_controller(request) + @params ||= {} + signature = request.signature + if signature && (expects = request.signature[:expects]) + (0..(@method_params.size-1)).each do |i| + if expects[i].is_a?(Hash) + @params[expects[i].keys[0].to_s] = @method_params[i] + else + @params['param%d' % i] = @method_params[i] + end + end + end + @params['action'] = request.method_name.to_s + @session ||= {} + @assigns ||= {} + return nil if before_action == false + true + end + + def after_direct_invoke(request) + after_direct_invoke_without_action_controller(request) + after_action + end + + def log_request(request) + unless logger.nil? || request.nil? + logger.debug("\nWeb Service Request:") + indented = request.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n") + logger.debug(indented) + end + end + + def log_response(response, elapsed) + unless logger.nil? || response.nil? + logger.debug("\nWeb Service Response (%f):" % elapsed) + indented = response.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n") + logger.debug(indented) + end + end + + unless method_defined?(:logger) + def logger; @logger; end + end + end + + module WsdlGeneration # :nodoc: + XsdNs = 'http://www.w3.org/2001/XMLSchema' + WsdlNs = 'http://schemas.xmlsoap.org/wsdl/' + SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/' + SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/' + SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http' + + def wsdl + case @request.method + when :get + begin + host_name = @request.env['HTTP_HOST'] || @request.env['SERVER_NAME'] + uri = "http://#{host_name}/#{controller_name}/" + soap_action_base = "/#{controller_name}" + xml = to_wsdl(self, uri, soap_action_base) + send_data(xml, :type => 'text/xml', :disposition => 'inline') + rescue Exception => e + log_error e unless logger.nil? + render_text('', "500 #{e.message}") + end + when :post + render_text('', "500 POST not supported") + end + end + + private + def to_wsdl(container, uri, soap_action_base) + wsdl = "" + + web_service_dispatching_mode = container.web_service_dispatching_mode + mapper = container.class.soap_mapper + namespace = mapper.custom_namespace + wsdl_service_name = namespace.split(/:/)[1] + + services = {} + mapper.map_container_services(container) do |name, api, api_methods| + services[name] = [api, api_methods] + end + custom_types = mapper.custom_types + + + xm = Builder::XmlMarkup.new(:target => wsdl, :indent => 2) + xm.instruct! + + xm.definitions('name' => wsdl_service_name, + 'targetNamespace' => namespace, + 'xmlns:typens' => namespace, + 'xmlns:xsd' => XsdNs, + 'xmlns:soap' => SoapNs, + 'xmlns:soapenc' => SoapEncodingNs, + 'xmlns:wsdl' => WsdlNs, + 'xmlns' => WsdlNs) do + + # Custom type XSD generation + if custom_types.size > 0 + xm.types do + xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do + custom_types.each do |klass, mapping| + case + when mapping.is_a?(ActionWebService::Protocol::Soap::SoapArrayMapping) + xm.xsd(:complexType, 'name' => mapping.type_name) do + xm.xsd(:complexContent) do + xm.xsd(:restriction, 'base' => 'soapenc:Array') do + xm.xsd(:attribute, 'ref' => 'soapenc:arrayType', + 'wsdl:arrayType' => mapping.element_mapping.qualified_type_name + '[]') + end + end + end + when mapping.is_a?(ActionWebService::Protocol::Soap::SoapMapping) + xm.xsd(:complexType, 'name' => mapping.type_name) do + xm.xsd(:all) do + mapping.each_attribute do |name, type_name| + xm.xsd(:element, 'name' => name, 'type' => type_name) + end + end + end + else + raise(WsdlError, "unsupported mapping type #{mapping.class.name}") + end + end + end + end + end + + services.each do |service_name, service_values| + service_api, api_methods = service_values + # Parameter list message definitions + api_methods.each do |method_name, method_signature| + gen = lambda do |msg_name, direction| + xm.message('name' => msg_name) do + sym = nil + if direction == :out + if method_signature[:returns] + xm.part('name' => 'return', 'type' => method_signature[:returns][0].qualified_type_name) + end + else + mapping_list = method_signature[:expects] + i = 1 + mapping_list.each do |mapping| + if mapping.is_a?(Hash) + param_name = mapping.keys.shift + mapping = mapping.values.shift + else + param_name = "param#{i}" + end + xm.part('name' => param_name, 'type' => mapping.qualified_type_name) + i += 1 + end if mapping_list + end + end + end + public_name = service_api.public_api_method_name(method_name) + gen.call(public_name, :in) + gen.call("#{public_name}Response", :out) + end + + # Declare the port + port_name = port_name_for(wsdl_service_name, service_name) + xm.portType('name' => port_name) do + api_methods.each do |method_name, method_signature| + public_name = service_api.public_api_method_name(method_name) + xm.operation('name' => public_name) do + xm.input('message' => "typens:#{public_name}") + xm.output('message' => "typens:#{public_name}Response") + end + end + end + + # Bind the port to SOAP + binding_name = binding_name_for(wsdl_service_name, service_name) + xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do + xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport) + api_methods.each do |method_name, method_signature| + public_name = service_api.public_api_method_name(method_name) + xm.operation('name' => public_name) do + case web_service_dispatching_mode + when :direct + soap_action = soap_action_base + "/api/" + public_name + when :delegated + soap_action = soap_action_base \ + + "/" + service_name.to_s \ + + "/" + public_name + end + xm.soap(:operation, 'soapAction' => soap_action) + xm.input do + xm.soap(:body, + 'use' => 'encoded', + 'namespace' => namespace, + 'encodingStyle' => SoapEncodingNs) + end + xm.output do + xm.soap(:body, + 'use' => 'encoded', + 'namespace' => namespace, + 'encodingStyle' => SoapEncodingNs) + end + end + end + end + end + + # Define the service + xm.service('name' => "#{wsdl_service_name}Service") do + services.each do |service_name, service_values| + port_name = port_name_for(wsdl_service_name, service_name) + binding_name = binding_name_for(wsdl_service_name, service_name) + case web_service_dispatching_mode + when :direct + binding_target = 'api' + when :delegated + binding_target = service_name.to_s + end + xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do + xm.soap(:address, 'location' => "#{uri}#{binding_target}") + end + end + end + end + end + + def port_name_for(wsdl_service_name, service_name) + "#{wsdl_service_name}#{service_name.to_s.camelize}Port" + end + + def binding_name_for(wsdl_service_name, service_name) + "#{wsdl_service_name}#{service_name.to_s.camelize}Binding" + end + end + end + end +end -- cgit v1.2.3