From e7a29380292902eae4799b2658507b3cfffb9cec Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 18 Feb 2005 10:35:25 +0000 Subject: Added Action Service to the repository git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@658 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../lib/action_service/router/action_controller.rb | 97 ++++++++++ actionservice/lib/action_service/router/wsdl.rb | 210 +++++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 actionservice/lib/action_service/router/action_controller.rb create mode 100644 actionservice/lib/action_service/router/wsdl.rb (limited to 'actionservice/lib/action_service/router') diff --git a/actionservice/lib/action_service/router/action_controller.rb b/actionservice/lib/action_service/router/action_controller.rb new file mode 100644 index 0000000000..01bd298bce --- /dev/null +++ b/actionservice/lib/action_service/router/action_controller.rb @@ -0,0 +1,97 @@ +module ActionService # :nodoc: + module Router # :nodoc: + module ActionController # :nodoc: + def self.append_features(base) # :nodoc: + base.add_service_api_callback do |container_class, api| + if container_class.service_dispatching_mode == :direct && !container_class.method_defined?(:api) + container_class.class_eval <<-EOS + def api + process_action_service_request + end + EOS + end + end + base.add_service_definition_callback do |klass, name, info| + if klass.service_dispatching_mode == :delegated + klass.class_eval <<-EOS + def #{name} + process_action_service_request + end + EOS + end + end + base.send(:include, ActionService::Router::ActionController::InstanceMethods) + end + + module InstanceMethods # :nodoc: + private + def process_action_service_request + protocol_request = nil + begin + begin + protocol_request = probe_request_protocol(self.request) + rescue Exception => e + logger.error "Invalid request: #{e.message}" + logger.error self.request.raw_post + raise + end + if protocol_request + log_request(protocol_request) + protocol_response = dispatch_service_request(protocol_request) + log_response(protocol_response) + response_options = { + :type => protocol_response.content_type, + :disposition => 'inline' + } + send_data(protocol_response.raw_body, response_options) + else + logger.fatal "Invalid Action Service service or method requested" + render_text 'Internal protocol error', "500 Invalid service/method" + end + rescue Exception => e + log_error e unless logger.nil? + exc_response = nil + case service_dispatching_mode + when :direct + if self.class.service_exception_reporting + exc_response = protocol_request.protocol.marshal_exception(e) + end + when :delegated + service_object = service_object(protocol_request.service_name) rescue nil + if service_object && service_object.class.service_exception_reporting + exc_response = protocol_request.protocol.marshal_exception(e) rescue nil + end + end + if exc_response + response_options = { + :type => exc_response.content_type, + :disposition => 'inline' + } + log_response exc_response + send_data(exc_response.raw_body, response_options) + else + render_text 'Internal protocol error', "500 #{e.message}" + end + end + end + + def log_request(protocol_request) + unless logger.nil? + service_name = protocol_request.service_name + method_name = protocol_request.public_method_name + logger.info "\nProcessing Action Service Request: #{service_name}##{method_name}" + logger.info "Raw Request Body:" + logger.info protocol_request.raw_body + end + end + + def log_response(protocol_response) + unless logger.nil? + logger.info "\nRaw Response Body:" + logger.info protocol_response.raw_body + end + end + end + end + end +end diff --git a/actionservice/lib/action_service/router/wsdl.rb b/actionservice/lib/action_service/router/wsdl.rb new file mode 100644 index 0000000000..ececa63322 --- /dev/null +++ b/actionservice/lib/action_service/router/wsdl.rb @@ -0,0 +1,210 @@ +module ActionService # :nodoc: + module Router # :nodoc: + module Wsdl # :nodoc: + def self.append_features(base) # :nodoc: + base.class_eval do + class << self + alias_method :inherited_without_wsdl, :inherited + end + end + base.extend(ClassMethods) + end + + module ClassMethods + def inherited(child) + inherited_without_wsdl(child) + child.send(:include, ActionService::Router::Wsdl::InstanceMethods) + end + end + + module InstanceMethods # :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 = "" + + service_dispatching_mode = container.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?(ActionService::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?(ActionService::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 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 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