Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

Changeset 1087

Show
Ignore:
Timestamp:
12/01/03 14:22:39 (5 years ago)
Author:
nahi
Message:

* introduce soap/property for optional parameter. driver#options...?

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/lib/soap/rpc/driver.rb

    r1058 r1087  
    1313require 'soap/rpc/element' 
    1414require 'soap/streamHandler' 
     15require 'soap/property' 
    1516 
    1617 
     
    2021 
    2122class Driver 
    22 public 
    2323  class EmptyResponseError < Error; end 
    2424 
    25   attr_accessor :mapping_registry 
    26   attr_accessor :soapaction 
    27   attr_reader :wiredump_dev 
    28   attr_reader :wiredump_file_base 
    29   attr_reader :streamhandler 
     25  class << self 
     26    def __attr_proxy(symbol, assignable = false) 
     27      name = symbol.to_s 
     28      module_eval <<-EOD 
     29        def #{name} 
     30          @servant.#{name} 
     31        end 
     32      EOD 
     33      if assignable 
     34        module_eval <<-EOD 
     35          def #{name}=(rhs) 
     36            @servant.#{name} = rhs 
     37          end 
     38        EOD 
     39      end 
     40    end 
     41  end 
     42 
     43  __attr_proxy :options 
     44  __attr_proxy :endpoint_url, true 
     45  __attr_proxy :mapping_registry, true 
     46  __attr_proxy :soapaction, true 
     47  __attr_proxy :default_encodingstyle, true 
     48 
     49  def httpproxy 
     50    @servant.options["client.streamhandler.http.proxy"] 
     51  end 
     52 
     53  def httpproxy=(httpproxy) 
     54    @servant.options["client.streamhandler.http.proxy"] = httpproxy 
     55  end 
     56 
     57  def mandatorycharset 
     58    @servant.options["client.streamhandler.mandatorycharset"] 
     59  end 
     60 
     61  def mandatorycharset=(mandatorycharset) 
     62    @servant.options["client.streamhandler.mandatorycharset"] = mandatorycharset 
     63  end 
     64 
     65  def wiredump_dev 
     66    @servant.options["client.streamhandler.wiredump_dev"] 
     67  end 
     68 
     69  def wiredump_dev=(wiredump_dev) 
     70    @servant.options["client.streamhandler.wiredump_dev"] = wiredump_dev 
     71  end 
     72 
     73  def wiredump_file_base 
     74    @servant.options["client.streamhandler.wiredump_file_base"] 
     75  end 
     76 
     77  def wiredump_file_base=(wiredump_file_base) 
     78    @servant.options["client.streamhandler.wiredump_file_base"] = 
     79      wiredump_file_base 
     80  end 
    3081 
    3182  def initialize(endpoint_url, namespace, soapaction = nil) 
    32     @namespace = namespace 
    33     @mapping_registry = nil      # for unmarshal 
    34     @soapaction = soapaction 
    35     @wiredump_dev = nil 
    36     @wiredump_file_base = nil 
    37     name = 'http_proxy' 
    38     @httpproxy = ENV[name] || ENV[name.upcase] 
    39     @streamhandler = HTTPPostStreamHandler.new(endpoint_url, @httpproxy, 
    40       XSD::Charset.encoding_label) 
    41     @proxy = Proxy.new(@streamhandler, @soapaction) 
    42     @proxy.allow_unqualified_element = true 
     83    @servant = Servant__.new(self, endpoint_url, namespace) 
     84    @servant.soapaction = soapaction 
     85    @proxy = @servant.proxy 
    4386  end 
    4487 
    4588  def inspect 
    46     "#<#{self.class}:#{@streamhandler.inspect}>" 
    47   end 
    48  
    49   def endpoint_url 
    50     @streamhandler.endpoint_url 
    51   end 
    52  
    53   def endpoint_url=(endpoint_url) 
    54     @streamhandler.endpoint_url = endpoint_url 
    55     @streamhandler.reset 
    56   end 
    57  
    58   def wiredump_dev=(dev) 
    59     @wiredump_dev = dev 
    60     @streamhandler.wiredump_dev = @wiredump_dev 
    61     @streamhandler.reset 
    62   end 
    63  
    64   def wiredump_file_base=(base) 
    65     @wiredump_file_base = base 
    66   end 
    67  
    68   def httpproxy 
    69     @httpproxy 
    70   end 
    71  
    72   def httpproxy=(httpproxy) 
    73     @httpproxy = httpproxy 
    74     @streamhandler.proxy = @httpproxy 
    75     @streamhandler.reset 
    76   end 
    77  
    78   def mandatorycharset 
    79     @proxy.mandatorycharset 
    80   end 
    81  
    82   def mandatorycharset=(mandatorycharset) 
    83     @proxy.mandatorycharset = mandatorycharset 
    84   end 
    85  
    86   def default_encodingstyle 
    87     @proxy.default_encodingstyle 
    88   end 
    89  
    90   def default_encodingstyle=(encodingstyle) 
    91     @proxy.default_encodingstyle = encodingstyle 
    92   end 
    93  
    94  
    95   ### 
    96   ## Method definition interfaces. 
    97   # 
    98   # params: [[param_def...]] or [paramname, paramname, ...] 
    99   # param_def: See proxy.rb.  Sorry. 
     89    "#<#{self.class}:#{@servant.streamhandler.inspect}>" 
     90  end 
    10091 
    10192  def add_method(name, *params) 
    102     add_method_with_soapaction_as(name, name, @soapaction, *params) 
     93    add_method_with_soapaction_as(name, name, @servant.soapaction, *params) 
    10394  end 
    10495 
    10596  def add_method_as(name, name_as, *params) 
    106     add_method_with_soapaction_as(name, name_as, @soapaction, *params) 
     97    add_method_with_soapaction_as(name, name_as, @servant.soapaction, *params) 
    10798  end 
    10899 
     
    117108        SOAPMethod.create_param_def(params) 
    118109      end 
    119     qname = XSD::QName.new(@namespace, name_as) 
    120     @proxy.add_method(qname, soapaction, name, param_def) 
    121     add_rpc_method_interface(name, param_def) 
    122   end 
    123  
    124  
    125   ### 
    126   ## Driving interface. 
    127   # 
     110    @servant.add_method(name_as, soapaction, name, param_def) 
     111  end 
     112 
     113  def reset_stream 
     114    @servant.streamhandler.reset 
     115  end 
     116 
    128117  def invoke(headers, body) 
    129     if @wiredump_file_base 
    130       @streamhandler.wiredump_file_base = 
    131         @wiredump_file_base + '_' << body.elename.name 
    132     end 
    133     @proxy.invoke(headers, body) 
     118    @servant.invoke(headers, body) 
    134119  end 
    135120 
    136121  def call(name, *params) 
    137     # Convert parameters: params array => SOAPArray => members array 
    138     params = Mapping.obj2soap(params, @mapping_registry).to_a 
    139     if @wiredump_file_base 
    140       @streamhandler.wiredump_file_base = @wiredump_file_base + '_' << name 
    141     end 
    142  
    143     # Then, call @proxy.call like the following. 
    144     header, body = @proxy.call(nil, name, *params) 
    145     unless body 
    146       raise EmptyResponseError.new("Empty response.") 
    147     end 
    148  
    149     begin 
    150       @proxy.check_fault(body) 
    151     rescue SOAP::FaultError => e 
    152       Mapping.fault2exception(e) 
    153     end 
    154  
    155     ret = body.response ? Mapping.soap2obj(body.response, @mapping_registry) : nil 
    156     if body.outparams 
    157       outparams = body.outparams.collect { |outparam| Mapping.soap2obj(outparam) } 
    158       return [ret].concat(outparams) 
    159     else 
    160       return ret 
    161     end 
    162   end 
    163  
    164   def reset_stream 
    165     @streamhandler.reset 
     122    @servant.call(name, *params) 
    166123  end 
    167124 
     
    169126 
    170127  def add_rpc_method_interface(name, param_def) 
    171     param_names = [] 
    172     i = 0 
    173     @proxy.method[name].each_param_name(RPC::SOAPMethod::IN, 
    174         RPC::SOAPMethod::INOUT) do |param_name| 
    175       i += 1 
    176       param_names << "arg#{ i }" 
    177     end 
    178  
    179     callparam = (param_names.collect { |pname| ", " + pname }).join 
    180     self.instance_eval <<-EOS 
    181       def #{ name }(#{ param_names.join(", ") }) 
    182         call("#{ name }"#{ callparam }) 
    183       end 
    184     EOS 
     128    @servant.add_rpc_method_interface(name, param_def) 
     129  end 
     130 
     131  class Servant__ 
     132    attr_reader :options 
     133    attr_reader :streamhandler 
     134    attr_reader :proxy 
     135 
     136    def initialize(host, endpoint_url, namespace) 
     137      @host = host 
     138      @namespace = namespace 
     139      @mapping_registry = nil      # for unmarshal 
     140      @soapaction = soapaction 
     141      @wiredump_file_base = nil 
     142      @options = ::SOAP::Property.new 
     143      set_options_hook 
     144      name = 'http_proxy' 
     145      httpproxy = ENV[name] || ENV[name.upcase] 
     146      @streamhandler = HTTPPostStreamHandler.new(endpoint_url, httpproxy, 
     147        XSD::Charset.encoding_label) 
     148      @proxy = Proxy.new(@streamhandler, @soapaction) 
     149      @proxy.allow_unqualified_element = true 
     150    end 
     151 
     152    def endpoint_url 
     153      @streamhandler.endpoint_url 
     154    end 
     155 
     156    def endpoint_url=(endpoint_url) 
     157      @streamhandler.endpoint_url = endpoint_url 
     158      @streamhandler.reset 
     159    end 
     160 
     161    def mapping_registry 
     162      @mapping_registry 
     163    end 
     164 
     165    def mapping_registry=(mapping_registry) 
     166      @mapping_registry = mapping_registry 
     167    end 
     168 
     169    def soapaction 
     170      @soapaction 
     171    end 
     172 
     173    def soapaction=(soapaction) 
     174      @soapaction = soapaction 
     175    end 
     176 
     177    def default_encodingstyle 
     178      @proxy.default_encodingstyle 
     179    end 
     180 
     181    def default_encodingstyle=(encodingstyle) 
     182      @proxy.default_encodingstyle = encodingstyle 
     183    end 
     184 
     185    def invoke(headers, body) 
     186      set_wiredump_file_base(body.elename.name) 
     187      @proxy.invoke(headers, body) 
     188    end 
     189 
     190    def call(name, *params) 
     191      set_wiredump_file_base(name) 
     192      # Convert parameters: params array => SOAPArray => members array 
     193      params = Mapping.obj2soap(params, @mapping_registry).to_a 
     194      header, body = @proxy.call(nil, name, *params) 
     195      raise EmptyResponseError.new("Empty response.") unless body 
     196      begin 
     197        @proxy.check_fault(body) 
     198      rescue SOAP::FaultError => e 
     199        Mapping.fault2exception(e) 
     200      end 
     201 
     202      ret = body.response ? 
     203        Mapping.soap2obj(body.response, @mapping_registry) : nil 
     204      if body.outparams 
     205        outparams = body.outparams.collect { |outparam| 
     206          Mapping.soap2obj(outparam) 
     207        } 
     208        return [ret].concat(outparams) 
     209      else 
     210        return ret 
     211      end 
     212    end 
     213 
     214    def add_method(name_as, soapaction, name, param_def) 
     215      qname = XSD::QName.new(@namespace, name_as) 
     216      @proxy.add_method(qname, soapaction, name, param_def) 
     217      add_rpc_method_interface(name, param_def) 
     218    end 
     219 
     220    def add_rpc_method_interface(name, param_def) 
     221      param_names = [] 
     222      i = 0 
     223      @proxy.method[name].each_param_name(RPC::SOAPMethod::IN, 
     224          RPC::SOAPMethod::INOUT) do |param_name| 
     225        i += 1 
     226        param_names << "arg#{ i }" 
     227      end 
     228      callparam = (param_names.collect { |pname| ", " + pname }).join 
     229      @host.instance_eval <<-EOS 
     230        def #{ name }(#{ param_names.join(", ") }) 
     231          @servant.call(#{ name.dump }#{ callparam }) 
     232        end 
     233      EOS 
     234    end 
     235 
     236  private 
     237 
     238    def set_wiredump_file_base(name) 
     239      if @wiredump_file_base 
     240        @streamhandler.wiredump_file_base = @wiredump_file_base + "_#{ name }" 
     241      end 
     242    end 
     243 
     244    def set_options_hook 
     245      @options.add_hook("client.streamhandler.http.proxy") do |key, value| 
     246        @streamhandler.proxy = value 
     247        @streamhandler.reset 
     248      end 
     249      @options.add_hook("client.streamhandler.mandatorycharset") do |key, value| 
     250        @proxy.mandatorycharset = value 
     251      end 
     252      @options.add_hook("client.streamhandler.wiredump_dev") do |key, value| 
     253        @streamhandler.wiredump_dev = value 
     254        @streamhandler.reset 
     255      end 
     256      @options.add_hook("client.streamhandler.wiredump_file_base") do |key, value| 
     257        @wiredump_file_base = value 
     258      end 
     259    end 
    185260  end 
    186261end 
  • trunk/lib/soap/wsdlDriver.rb

    r1058 r1087  
    3838  end 
    3939 
    40   def create_driver(servicename = nil, portname = nil, opt = {}
     40  def create_driver(servicename = nil, portname = nil
    4141    service = if servicename 
    4242        @wsdl.service(XSD::QName.new(@wsdl.targetnamespace, servicename)) 
     
    5858      raise FactoryError.new("soap:address element not found in WSDL.") 
    5959    end 
    60     WSDLDriver.new(@wsdl, port, @logdev, opt
     60    WSDLDriver.new(@wsdl, port, @logdev
    6161  end 
    6262 
     
    9191  end 
    9292 
    93   __attr_proxy :opt 
    94   __attr_proxy :logdev, true 
     93  __attr_proxy :options 
     94  __attr_proxy :endpoint_url, true 
    9595  __attr_proxy :mapping_registry, true          # for RPC unmarshal 
    9696  __attr_proxy :wsdl_mapping_registry, true     # for RPC marshal 
    97   __attr_proxy :endpoint_url, true 
    98   __attr_proxy :wiredump_dev, true 
    99   __attr_proxy :wiredump_file_base, true 
    100   __attr_proxy :httpproxy, true 
    101   __attr_proxy :mandatorycharset, true          # force using charset 
    102  
    10397  __attr_proxy :default_encodingstyle, true 
    10498  __attr_proxy :allow_unqualified_element, true 
    10599  __attr_proxy :generate_explicit_type, true 
    106100 
     101  def httpproxy 
     102    @servant.options["client.streamhandler.http.proxy"] 
     103  end 
     104 
     105  def httpproxy=(httpproxy) 
     106    @servant.options["client.streamhandler.http.proxy"] = httpproxy 
     107  end 
     108 
     109  def mandatorycharset 
     110    @servant.options["client.streamhandler.mandatorycharset"] 
     111  end 
     112 
     113  def mandatorycharset=(mandatorycharset) 
     114    @servant.options["client.streamhandler.mandatorycharset"] = mandatorycharset 
     115  end 
     116 
     117  def wiredump_dev 
     118    @servant.options["client.streamhandler.wiredump_dev"] 
     119  end 
     120 
     121  def wiredump_dev=(wiredump_dev) 
     122    @servant.options["client.streamhandler.wiredump_dev"] = wiredump_dev 
     123  end 
     124 
     125  def wiredump_file_base 
     126    @servant.options["client.streamhandler.wiredump_file_base"] 
     127  end 
     128 
     129  def wiredump_file_base=(wiredump_file_base) 
     130    @servant.options["client.streamhandler.wiredump_file_base"] = 
     131      wiredump_file_base 
     132  end 
     133 
     134  def initialize(wsdl, port, logdev) 
     135    @servant = Servant__.new(self, wsdl, port, logdev) 
     136  end 
     137 
     138  def inspect 
     139    "#<#{self.class}:#{@servant.port.name}>" 
     140  end 
     141 
    107142  def reset_stream 
    108     @servant.reset_stream 
     143    @servant.streamhandler.reset 
    109144  end 
    110145 
     
    116151    include SOAP 
    117152 
    118     attr_reader :wsdl 
     153    attr_reader :options 
     154    attr_reader :streamhandler 
    119155    attr_reader :port 
    120     attr_reader :opt 
    121     attr_accessor :logdev 
     156 
    122157    attr_accessor :mapping_registry 
    123158    attr_accessor :wsdl_mapping_registry 
    124     attr_reader :wiredump_dev 
    125     attr_reader :wiredump_file_base 
    126     attr_reader :httpproxy 
    127     attr_accessor :mandatorycharset 
    128  
    129159    attr_accessor :default_encodingstyle 
    130160    attr_accessor :allow_unqualified_element 
    131161    attr_accessor :generate_explicit_type 
     162 
     163    def initialize(host, wsdl, port, logdev) 
     164      @host = host 
     165      @wsdl = wsdl 
     166      @port = port 
     167      @logdev = logdev 
     168 
     169      @options = ::SOAP::Property.new 
     170      set_options_hook 
     171      @mapping_registry = nil           # for rpc unmarshal 
     172      @wsdl_mapping_registry = nil      # for rpc marshal 
     173      @default_encodingstyle = EncodingNamespace 
     174      @allow_unqualified_element = true 
     175      @generate_explicit_type = false 
     176      @wiredump_file_base = nil 
     177      @mandatorycharset = nil 
     178 
     179      @wsdl_elements = @wsdl.collect_elements 
     180      @wsdl_types = @wsdl.collect_complextypes 
     181      @rpc_decode_typemap = @wsdl_types + @wsdl.soap_rpc_complextypes(port.find_binding) 
     182      @wsdl_mapping_registry = Mapping::WSDLRegistry.new(@rpc_decode_typemap) 
     183      @doc_mapper = Mapper.new(@wsdl_elements, @wsdl_types) 
     184 
     185      @streamhandler = HTTPPostStreamHandler.new(@port.soap_address.location, 
     186        ENV['http_proxy'] || ENV['HTTP_PROXY'], XSD::Charset.encoding_label) 
     187      @operations = {} 
     188      # Convert a map which key is QName, to a Hash which key is String. 
     189      @port.inputoperation_map.each do |op_name, op_info| 
     190        @operations[op_name.name] = op_info 
     191        add_method_interface(op_info) 
     192      end 
     193    end 
     194 
     195    def endpoint_url 
     196      @streamhandler.endpoint_url 
     197    end 
     198 
     199    def endpoint_url=(endpoint_url) 
     200      @streamhandler.endpoint_url = endpoint_url 
     201      @streamhandler.reset 
     202    end 
     203 
     204    def rpc_send(method_name, *params) 
     205      log(INFO) { "call: calling method '#{ method_name }'." } 
     206      log(DEBUG) { "call: parameters '#{ params.inspect }'." } 
     207 
     208      op_info = @operations[method_name] 
     209      parts_names = op_info.bodyparts.collect { |part| part.name } 
     210      obj = create_method_obj(parts_names, params) 
     211      method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.optype_name) 
     212      method.elename = op_info.op_name 
     213      method.type = XSD::QName.new      # Request should not be typed. 
     214      req_header = nil 
     215      req_body = SOAPBody.new(method) 
     216 
     217      if @wiredump_file_base 
     218        @streamhandler.wiredump_file_base = 
     219          @wiredump_file_base + '_' << method_name 
     220      end 
     221 
     222      begin 
     223        opt = create_options 
     224        opt[:decode_typemap] = @rpc_decode_typemap 
     225        res_header, res_body = invoke(req_header, req_body, op_info, opt) 
     226        if res_body.fault 
     227          raise SOAP::FaultError.new(res_body.fault) 
     228        end 
     229      rescue SOAP::FaultError => e 
     230        Mapping.fault2exception(e) 
     231      end 
     232 
     233      ret = res_body.response ? 
     234        Mapping.soap2obj(res_body.response, @mapping_registry) : nil 
     235 
     236      if res_body.outparams 
     237        outparams = res_body.outparams.collect { |outparam| 
     238          Mapping.soap2obj(outparam) 
     239        } 
     240        return [ret].concat(outparams) 
     241      else 
     242        return ret 
     243      end 
     244    end 
     245 
     246    # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...] 
     247    # req_body: SOAPBasetype/SOAPCompoundtype 
     248    def document_send(name, header_obj, body_obj) 
     249      log(INFO) { "document_send: sending document '#{ name }'." } 
     250      op_info = @operations[name] 
     251      req_header = header_from_obj(header_obj, op_info) 
     252      req_body = body_from_obj(body_obj, op_info) 
     253      opt = create_options 
     254      res_header, res_body = invoke(req_header, req_body, op_info, opt) 
     255      if res_body.fault 
     256        raise SOAP::FaultError.new(res_body.fault) 
     257      end 
     258      res_body_obj = res_body.response ? 
     259        Mapping.soap2obj(res_body.response, @mapping_registry) : nil 
     260      return res_header, res_body_obj 
     261    end 
     262 
     263  private 
     264 
     265    def create_method_obj(names, params) 
     266      o = Object.new 
     267      for idx in 0 ... params.length 
     268        o.instance_eval("@#{ names[idx] } = params[idx]") 
     269      end 
     270      o 
     271    end 
     272 
     273    def invoke(req_header, req_body, op_info, opt) 
     274      send_string = Processor.marshal(req_header, req_body, opt) 
     275      log(DEBUG) { "invoke: sending string #{ send_string }" } 
     276      data = @streamhandler.send(send_string, op_info.soapaction) 
     277      log(DEBUG) { "invoke: received string #{ data.receive_string }" } 
     278      if data.receive_string.empty? 
     279        return nil, nil 
     280      end 
     281      opt[:charset] = @mandatorycharset || 
     282        StreamHandler.parse_media_type(data.receive_contenttype) 
     283      res_header, res_body = Processor.unmarshal(data.receive_string, opt) 
     284      return res_header, res_body 
     285    end 
     286 
     287    def header_from_obj(obj, op_info) 
     288      if obj.is_a?(SOAPHeader) 
     289        obj 
     290      elsif op_info.headerparts.empty? 
     291        if obj.nil? 
     292          nil 
     293        else 
     294          raise RuntimeError.new("No header definition in schema.") 
     295        end 
     296      elsif op_info.headerparts.size == 1 
     297        part = op_info.headerparts[0] 
     298        header = SOAPHeader.new() 
     299        header.add(headeritem_from_obj(obj, part.element || part.eletype)) 
     300        header 
     301      else 
     302        header = SOAPHeader.new() 
     303        op_info.headerparts.each do |part| 
     304          child = obj[part.elename.name] 
     305          ele = headeritem_from_obj(child, part.element || part.eletype) 
     306          header.add(ele) 
     307        end 
     308        header 
     309      end 
     310    end 
     311 
     312    def headeritem_from_obj(obj, name) 
     313      if obj.nil? 
     314        SOAPElement.new(name) 
     315      elsif obj.is_a?(SOAPHeaderItem) 
     316        obj 
     317      else 
     318        @doc_mapper.obj2ele(obj, name) 
     319      end 
     320    end 
     321 
     322    def body_from_obj(obj, op_info) 
     323      if obj.is_a?(SOAPBody) 
     324        obj 
     325      elsif op_info.bodyparts.empty? 
     326        if obj.nil? 
     327          nil 
     328        else 
     329          raise RuntimeError.new("No body found in schema.") 
     330        end 
     331      elsif op_info.bodyparts.size == 1 
     332        part = op_info.bodyparts[0] 
     333        ele = bodyitem_from_obj(obj, part.element || part.type) 
     334        SOAPBody.new(ele) 
     335      else 
     336        body = SOAPBody.new 
     337        op_info.bodyparts.each do |part| 
     338          child = obj[part.elename.name] 
     339          ele = bodyitem_from_obj(child, part.element || part.type) 
     340          body.add(ele.elename.name, ele) 
     341        end 
     342        body 
     343      end 
     344    end 
     345 
     346    def bodyitem_from_obj(obj, name) 
     347      if obj.nil? 
     348        SOAPElement.new(name) 
     349      elsif obj.is_a?(SOAPElement) 
     350        obj 
     351      else 
     352        @doc_mapper.obj2ele(obj, name) 
     353      end 
     354    end 
     355 
     356    def add_method_interface(op_info) 
     357      case op_info.style 
     358      when :document 
     359        add_document_method_interface(op_info.op_name.name) 
     360      when :rpc 
     361        parts_names = op_info.bodyparts.collect { |part| part.name } 
     362        add_rpc_method_interface(op_info.op_name.name, parts_names) 
     363      else 
     364        raise RuntimeError.new("Unknown style: #{op_info.style}") 
     365      end 
     366    end 
     367 
     368    def add_document_method_interface(name) 
     369      @host.instance_eval <<-EOS 
     370        def #{ name }(headers, body) 
     371          @servant.document_send(#{ name.dump }, headers, body) 
     372        end 
     373      EOS 
     374    end 
     375 
     376    def add_rpc_method_interface(name, parts_names) 
     377      i = 0 
     378      param_names = parts_names.collect { |orgname| i += 1; "arg#{ i }" } 
     379      callparam = (param_names.collect { |pname| ", " + pname }).join 
     380      @host.instance_eval <<-EOS 
     381        def #{ name }(#{ param_names.join(", ") }) 
     382          @servant.rpc_send(#{ name.dump }#{ callparam }) 
     383        end 
     384      EOS 
     385    end 
     386 
     387    def create_options 
     388      opt = {} 
     389      opt[:default_encodingstyle] = @default_encodingstyle 
     390      opt[:allow_unqualified_element] = @allow_unqualified_element 
     391      opt[:generate_explicit_type] = @generate_explicit_type 
     392      opt 
     393    end 
     394 
     395    def log(sev) 
     396      @logdev.add(sev, nil, self.class) { yield } if @logdev 
     397    end 
     398 
     399    def set_options_hook 
     400      @options.add_hook("client.streamhandler.http.proxy") do |key, value| 
     401        @streamhandler.proxy = value 
     402        @streamhandler.reset 
     403      end 
     404      @options.add_hook("client.streamhandler.mandatorycharset") do |key, value| 
     405        @mandatorycharset = value 
     406      end 
     407      @options.add_hook("client.streamhandler.wiredump_dev") do |key, value| 
     408        @streamhandler.wiredump_dev = value 
     409        @streamhandler.reset 
     410      end 
     411      @options.add_hook("client.streamhandler.wiredump_file_base") do |key, value| 
     412        @wiredump_file_base = value 
     413      end 
     414    end 
    132415 
    133416    class Mapper 
     
    208491      end 
    209492    end 
    210  
    211     def initialize(host, wsdl, port, logdev, opt) 
    212       @host = host 
    213       @wsdl = wsdl 
    214       @port = port 
    215       @logdev = logdev 
    216       @opt = opt.dup 
    217       @mapping_registry = nil           # for rpc unmarshal 
    218       @wsdl_mapping_registry = nil      # for rpc marshal 
    219       @wiredump_dev = nil 
    220       @wiredump_file_base = nil 
    221       @mandatorycharset = nil 
    222  
    223       @wsdl_elements = @wsdl.collect_elements 
    224       @wsdl_types = @wsdl.collect_complextypes 
    225       @rpc_decode_typemap = @wsdl_types + @wsdl.soap_rpc_complextypes(port.find_binding) 
    226       @wsdl_mapping_registry = Mapping::WSDLRegistry.new(@rpc_decode_typemap) 
    227       @doc_mapper = Mapper.new(@wsdl_elements, @wsdl_types) 
    228       @default_encodingstyle = EncodingNamespace 
    229       @allow_unqualified_element = true 
    230       @generate_explicit_type = false 
    231  
    232       create_streamhandler(@port.soap_address.location, 
    233         ENV['http_proxy'] || ENV['HTTP_PROXY']) 
    234       @operations = {} 
    235       # Convert a map which key is QName, to a Hash which key is String. 
    236       @port.inputoperation_map.each do |op_name, op_info| 
    237         @operations[op_name.name] = op_info 
    238         add_method_interface(op_info) 
    239       end 
    240     end 
    241  
    242     def endpoint_url 
    243       @streamhandler.endpoint_url 
    244     end 
    245  
    246     def endpoint_url=(endpoint_url) 
    247       @streamhandler.endpoint_url = endpoint_url 
    248       @streamhandler.reset 
    249       log(DEBUG) { "endpoint_url=: set endpoint_url #{ endpoint_url }." } 
    250     end 
    251  
    252     def wiredump_dev=(dev) 
    253       @wiredump_dev = dev 
    254       @streamhandler.wiredump_dev = @wiredump_dev 
    255       @streamhandler.reset 
    256     end 
    257  
    258     def wiredump_file_base=(base) 
    259       @wiredump_file_base = base 
    260     end 
    261  
    262     def httpproxy=(httpproxy) 
    263       @streamhandler.proxy = httpproxy 
    264       @streamhandler.reset 
    265       log(DEBUG) { "httpproxy=: set httpproxy #{ httpproxy }." } 
    266     end 
    267  
    268     def reset_stream 
    269       @streamhandler.reset 
    270     end 
    271  
    272     def rpc_send(method_name, *params) 
    273       log(INFO) { "call: calling method '#{ method_name }'." } 
    274       log(DEBUG) { "call: parameters '#{ params.inspect }'." } 
    275  
    276       op_info = @operations[method_name] 
    277       parts_names = op_info.bodyparts.collect { |part| part.name } 
    278       obj = create_method_obj(parts_names, params) 
    279       method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.optype_name) 
    280       method.elename = op_info.op_name 
    281       method.type = XSD::QName.new      # Request should not be typed. 
    282       req_header = nil 
    283       req_body = SOAPBody.new(method) 
    284  
    285       if @wiredump_file_base 
    286         @streamhandler.wiredump_file_base = 
    287           @wiredump_file_base + '_' << method_name 
    288       end 
    289  
    290       begin 
    291         opt = create_options 
    292         opt[:decode_typemap] = @rpc_decode_typemap 
    293         res_header, res_body = invoke(req_header, req_body, op_info, opt) 
    294         if res_body.fault 
    295           raise SOAP::FaultError.new(res_body.fault) 
    296         end 
    297       rescue SOAP::FaultError => e 
    298         Mapping.fault2exception(e) 
    299       end 
    300  
    301       ret = res_body.response ? 
    302         Mapping.soap2obj(res_body.response, @mapping_registry) : nil 
    303  
    304       if res_body.outparams 
    305         outparams = res_body.outparams.collect { |outparam| 
    306           Mapping.soap2obj(outparam) 
    307         } 
    308         return [ret].concat(outparams) 
    309       else 
    310         return ret 
    311       end 
    312     end 
    313  
    314     # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...] 
    315     # req_body: SOAPBasetype/SOAPCompoundtype 
    316     def document_send(name, header_obj, body_obj) 
    317       log(INFO) { "document_send: sending document '#{ name }'." } 
    318       op_info = @operations[name] 
    319       req_header = header_from_obj(header_obj, op_info) 
    320       req_body = body_from_obj(body_obj, op_info) 
    321       opt = create_options 
    322       res_header, res_body = invoke(req_header, req_body, op_info, opt) 
    323       if res_body.fault 
    324         raise SOAP::FaultError.new(res_body.fault) 
    325       end 
    326       res_body_obj = res_body.response ? 
    327         Mapping.soap2obj(res_body.response, @mapping_registry) : nil 
    328       return res_header, res_body_obj 
    329     end 
    330  
    331   private 
    332  
    333     def create_streamhandler(endpoint_url, httpproxy) 
    334       @streamhandler = HTTPPostStreamHandler.new(endpoint_url, httpproxy, 
    335         XSD::Charset.encoding_label) 
    336       @streamhandler.wiredump_dev = @wiredump_dev 
    337     end 
    338  
    339     def create_method_obj(names, params) 
    340       o = Object.new 
    341       for idx in 0 ... params.length 
    342         o.instance_eval("@#{ names[idx] } = params[idx]") 
    343       end 
    344       o 
    345     end 
    346  
    347     def invoke(req_header, req_body, op_info, opt) 
    348       send_string = Processor.marshal(req_header, req_body, opt) 
    349       log(DEBUG) { "invoke: sending string #{ send_string }" } 
    350       data = @streamhandler.send(send_string, op_info.soapaction) 
    351       log(DEBUG) { "invoke: received string #{ data.receive_string }" } 
    352       if data.receive_string.empty? 
    353         return nil, nil 
    354       end 
    355       opt[:charset] = @mandatorycharset || 
    356         StreamHandler.parse_media_type(data.receive_contenttype) 
    357       res_header, res_body = Processor.unmarshal(data.receive_string, opt) 
    358       return res_header, res_body 
    359     end 
    360  
    361     def header_from_obj(obj, op_info) 
    362       if obj.is_a?(SOAPHeader) 
    363         obj 
    364       elsif op_info.headerparts.empty? 
    365         if obj.nil? 
    366           nil 
    367         else 
    368           raise RuntimeError.new("No header definition in schema.") 
    369         end 
    370       elsif op_info.headerparts.size == 1 
    371         part = op_info.headerparts[0] 
    372         header = SOAPHeader.new() 
    373         header.add(headeritem_from_obj(obj, part.element || part.eletype)) 
    374         header 
    375       else 
    376         header = SOAPHeader.new() 
    377         op_info.headerparts.each do |part| 
    378           child = obj[part.elename.name] 
    379           ele = headeritem_from_obj(child, part.element || part.eletype) 
    380           header.add(ele) 
    381         end 
    382         header 
    383       end 
    384     end 
    385  
    386     def headeritem_from_obj(obj, name) 
    387       if obj.nil? 
    388         SOAPElement.new(name) 
    389       elsif obj.is_a?(SOAPHeaderItem) 
    390         obj 
    391       else 
    392         @doc_mapper.obj2ele(obj, name) 
    393       end 
    394     end 
    395  
    396     def body_from_obj(obj, op_info) 
    397       if obj.is_a?(SOAPBody) 
    398         obj 
    399       elsif op_info.bodyparts.empty? 
    400         if obj.nil? 
    401           nil 
    402         else 
    403           raise RuntimeError.new("No body found in schema.") 
    404         end 
    405       elsif op_info.bodyparts.size == 1 
    406         part = op_info.bodyparts[0] 
    407         ele = bodyitem_from_obj(obj, part.element || part.type) 
    408         SOAPBody.new(ele) 
    409       else 
    410         body = SOAPBody.new 
    411         op_info.bodyparts.each do |part| 
    412           child = obj[part.elename.name] 
    413           ele = bodyitem_from_obj(child, part.element || part.type) 
    414           body.add(ele.elename.name, ele) 
    415         end 
    416         body 
    417       end 
    418     end 
    419  
    420     def bodyitem_from_obj(obj, name) 
    421       if obj.nil? 
    422         SOAPElement.new(name) 
    423       elsif obj.is_a?(SOAPElement) 
    424         obj 
    425       else 
    426         @doc_mapper.obj2ele(obj, name) 
    427       end 
    428     end 
    429  
    430     def add_method_interface(op_info) 
    431       case op_info.style 
    432       when :document 
    433         add_document_method_interface(op_info.op_name.name) 
    434       when :rpc 
    435         parts_names = op_info.bodyparts.collect { |part| part.name } 
    436         add_rpc_method_interface(op_info.op_name.name, parts_names) 
    437       else 
    438         raise RuntimeError.new("Unknown style: #{op_info.style}") 
    439       end 
    440     end 
    441  
    442     def add_document_method_interface(name) 
    443       @host.instance_eval <<-EOS 
    444         def #{ name }(headers, body) 
    445           @servant.document_send(#{ name.dump }, headers, body) 
    446         end 
    447       EOS 
    448     end 
    449  
    450     def add_rpc_method_interface(name, parts_names) 
    451       i = 0 
    452       param_names = parts_names.collect { |orgname| i += 1; "arg#{ i }" } 
    453       callparam_str = (param_names.collect { |pname| ", " + pname }).join 
    454       @host.instance_eval <<-EOS 
    455         def #{ name }(#{ param_names.join(", ") }) 
    456           @servant.rpc_send(#{ name.dump }#{ callparam_str }) 
    457         end 
    458       EOS 
    459     end 
    460  
    461     def create_options 
    462       opt = @opt.dup 
    463       opt[:default_encodingstyle] = @default_encodingstyle 
    464       opt[:allow_unqualified_element] = @allow_unqualified_element 
    465       opt[:generate_explicit_type] = @generate_explicit_type 
    466       opt 
    467     end 
    468  
    469     def log(sev) 
    470       @logdev.add(sev, nil, self.class) { yield } if @logdev 
    471     end 
    472   end 
    473  
    474   def initialize(wsdl, port, logdev, opt) 
    475     @servant = Servant__.new(self, wsdl, port, logdev, opt) 
    476   end 
    477  
    478   def inspect 
    479     "#<#{self.class}:#{@servant.port.name}>" 
    480493  end 
    481494end