aboutsummaryrefslogblamecommitdiffstats
path: root/actionservice/lib/action_service/router/wsdl.rb
blob: ececa63322ede61a9088e76108591b1d00352ad0 (plain) (tree)

















































































































































































































                                                                                                                        
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