source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/soap/wsdlDriver.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 15.8 KB
Line 
1# SOAP4R - SOAP WSDL driver
2# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <[email protected]>.
3
4# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5# redistribute it and/or modify it under the same terms of Ruby's license;
6# either the dual license version in 2003, or any later version.
7
8
9require 'wsdl/parser'
10require 'wsdl/importer'
11require 'xsd/qname'
12require 'xsd/codegen/gensupport'
13require 'soap/mapping/wsdlencodedregistry'
14require 'soap/mapping/wsdlliteralregistry'
15require 'soap/rpc/driver'
16require 'wsdl/soap/methodDefCreator'
17
18
19module SOAP
20
21
22class WSDLDriverFactory
23 class FactoryError < StandardError; end
24
25 attr_reader :wsdl
26
27 def initialize(wsdl)
28 @wsdl = import(wsdl)
29 @methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@wsdl)
30 end
31
32 def inspect
33 "#<#{self.class}:#{@wsdl.name}>"
34 end
35
36 def create_rpc_driver(servicename = nil, portname = nil)
37 port = find_port(servicename, portname)
38 drv = SOAP::RPC::Driver.new(port.soap_address.location)
39 init_driver(drv, port)
40 add_operation(drv, port)
41 drv
42 end
43
44 # depricated old interface
45 def create_driver(servicename = nil, portname = nil)
46 warn("WSDLDriverFactory#create_driver is depricated. Use create_rpc_driver instead.")
47 port = find_port(servicename, portname)
48 WSDLDriver.new(@wsdl, port, nil)
49 end
50
51 # Backward compatibility.
52 alias createDriver create_driver
53
54private
55
56 def find_port(servicename = nil, portname = nil)
57 service = port = nil
58 if servicename
59 service = @wsdl.service(
60 XSD::QName.new(@wsdl.targetnamespace, servicename))
61 else
62 service = @wsdl.services[0]
63 end
64 if service.nil?
65 raise FactoryError.new("service #{servicename} not found in WSDL")
66 end
67 if portname
68 port = service.ports[XSD::QName.new(@wsdl.targetnamespace, portname)]
69 if port.nil?
70 raise FactoryError.new("port #{portname} not found in WSDL")
71 end
72 else
73 port = service.ports.find { |port| !port.soap_address.nil? }
74 if port.nil?
75 raise FactoryError.new("no ports have soap:address")
76 end
77 end
78 if port.soap_address.nil?
79 raise FactoryError.new("soap:address element not found in WSDL")
80 end
81 port
82 end
83
84 def init_driver(drv, port)
85 wsdl_elements = @wsdl.collect_elements
86 wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
87 rpc_decode_typemap = wsdl_types +
88 @wsdl.soap_rpc_complextypes(port.find_binding)
89 drv.proxy.mapping_registry =
90 Mapping::WSDLEncodedRegistry.new(rpc_decode_typemap)
91 drv.proxy.literal_mapping_registry =
92 Mapping::WSDLLiteralRegistry.new(wsdl_types, wsdl_elements)
93 end
94
95 def add_operation(drv, port)
96 port.find_binding.operations.each do |op_bind|
97 op_name = op_bind.soapoperation_name
98 soapaction = op_bind.soapaction || ''
99 orgname = op_name.name
100 name = XSD::CodeGen::GenSupport.safemethodname(orgname)
101 param_def = create_param_def(op_bind)
102 opt = {
103 :request_style => op_bind.soapoperation_style,
104 :response_style => op_bind.soapoperation_style,
105 :request_use => op_bind.input.soapbody_use,
106 :response_use => op_bind.output.soapbody_use,
107 :elementformdefault => false,
108 :attributeformdefault => false
109 }
110 if op_bind.soapoperation_style == :rpc
111 drv.add_rpc_operation(op_name, soapaction, name, param_def, opt)
112 else
113 drv.add_document_operation(soapaction, name, param_def, opt)
114 end
115 if orgname != name and orgname.capitalize == name.capitalize
116 ::SOAP::Mapping.define_singleton_method(drv, orgname) do |*arg|
117 __send__(name, *arg)
118 end
119 end
120 end
121 end
122
123 def import(location)
124 WSDL::Importer.import(location)
125 end
126
127 def create_param_def(op_bind)
128 op = op_bind.find_operation
129 if op_bind.soapoperation_style == :rpc
130 param_def = @methoddefcreator.collect_rpcparameter(op)
131 else
132 param_def = @methoddefcreator.collect_documentparameter(op)
133 end
134 # the first element of typedef in param_def is a String like
135 # "::SOAP::SOAPStruct". turn this String to a class.
136 param_def.collect { |io, name, typedef|
137 typedef[0] = Mapping.class_from_name(typedef[0])
138 [io, name, typedef]
139 }
140 end
141
142 def partqname(part)
143 if part.type
144 part.type
145 else
146 part.element
147 end
148 end
149
150 def param_def(type, name, klass, partqname)
151 [type, name, [klass, partqname.namespace, partqname.name]]
152 end
153
154 def filter_parts(partsdef, partssource)
155 parts = partsdef.split(/\s+/)
156 partssource.find_all { |part| parts.include?(part.name) }
157 end
158end
159
160
161class WSDLDriver
162 class << self
163 if RUBY_VERSION >= "1.7.0"
164 def __attr_proxy(symbol, assignable = false)
165 name = symbol.to_s
166 define_method(name) {
167 @servant.__send__(name)
168 }
169 if assignable
170 aname = name + '='
171 define_method(aname) { |rhs|
172 @servant.__send__(aname, rhs)
173 }
174 end
175 end
176 else
177 def __attr_proxy(symbol, assignable = false)
178 name = symbol.to_s
179 module_eval <<-EOS
180 def #{name}
181 @servant.#{name}
182 end
183 EOS
184 if assignable
185 module_eval <<-EOS
186 def #{name}=(value)
187 @servant.#{name} = value
188 end
189 EOS
190 end
191 end
192 end
193 end
194
195 __attr_proxy :options
196 __attr_proxy :headerhandler
197 __attr_proxy :streamhandler
198 __attr_proxy :test_loopback_response
199 __attr_proxy :endpoint_url, true
200 __attr_proxy :mapping_registry, true # for RPC unmarshal
201 __attr_proxy :wsdl_mapping_registry, true # for RPC marshal
202 __attr_proxy :default_encodingstyle, true
203 __attr_proxy :generate_explicit_type, true
204 __attr_proxy :allow_unqualified_element, true
205
206 def httpproxy
207 @servant.options["protocol.http.proxy"]
208 end
209
210 def httpproxy=(httpproxy)
211 @servant.options["protocol.http.proxy"] = httpproxy
212 end
213
214 def wiredump_dev
215 @servant.options["protocol.http.wiredump_dev"]
216 end
217
218 def wiredump_dev=(wiredump_dev)
219 @servant.options["protocol.http.wiredump_dev"] = wiredump_dev
220 end
221
222 def mandatorycharset
223 @servant.options["protocol.mandatorycharset"]
224 end
225
226 def mandatorycharset=(mandatorycharset)
227 @servant.options["protocol.mandatorycharset"] = mandatorycharset
228 end
229
230 def wiredump_file_base
231 @servant.options["protocol.wiredump_file_base"]
232 end
233
234 def wiredump_file_base=(wiredump_file_base)
235 @servant.options["protocol.wiredump_file_base"] = wiredump_file_base
236 end
237
238 def initialize(wsdl, port, logdev)
239 @servant = Servant__.new(self, wsdl, port, logdev)
240 end
241
242 def inspect
243 "#<#{self.class}:#{@servant.port.name}>"
244 end
245
246 def reset_stream
247 @servant.reset_stream
248 end
249
250 # Backward compatibility.
251 alias generateEncodeType= generate_explicit_type=
252
253 class Servant__
254 include SOAP
255
256 attr_reader :options
257 attr_reader :port
258
259 attr_accessor :soapaction
260 attr_accessor :default_encodingstyle
261 attr_accessor :allow_unqualified_element
262 attr_accessor :generate_explicit_type
263 attr_accessor :mapping_registry
264 attr_accessor :wsdl_mapping_registry
265
266 def initialize(host, wsdl, port, logdev)
267 @host = host
268 @wsdl = wsdl
269 @port = port
270 @logdev = logdev
271 @soapaction = nil
272 @options = setup_options
273 @default_encodingstyle = nil
274 @allow_unqualified_element = nil
275 @generate_explicit_type = false
276 @mapping_registry = nil # for rpc unmarshal
277 @wsdl_mapping_registry = nil # for rpc marshal
278 @wiredump_file_base = nil
279 @mandatorycharset = nil
280 @wsdl_elements = @wsdl.collect_elements
281 @wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
282 @rpc_decode_typemap = @wsdl_types +
283 @wsdl.soap_rpc_complextypes(port.find_binding)
284 @wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(
285 @rpc_decode_typemap)
286 @doc_mapper = Mapping::WSDLLiteralRegistry.new(
287 @wsdl_types, @wsdl_elements)
288 endpoint_url = @port.soap_address.location
289 # Convert a map which key is QName, to a Hash which key is String.
290 @operation = {}
291 @port.inputoperation_map.each do |op_name, op_info|
292 orgname = op_name.name
293 name = XSD::CodeGen::GenSupport.safemethodname(orgname)
294 @operation[name] = @operation[orgname] = op_info
295 add_method_interface(op_info)
296 end
297 @proxy = ::SOAP::RPC::Proxy.new(endpoint_url, @soapaction, @options)
298 end
299
300 def inspect
301 "#<#{self.class}:#{@proxy.inspect}>"
302 end
303
304 def endpoint_url
305 @proxy.endpoint_url
306 end
307
308 def endpoint_url=(endpoint_url)
309 @proxy.endpoint_url = endpoint_url
310 end
311
312 def headerhandler
313 @proxy.headerhandler
314 end
315
316 def streamhandler
317 @proxy.streamhandler
318 end
319
320 def test_loopback_response
321 @proxy.test_loopback_response
322 end
323
324 def reset_stream
325 @proxy.reset_stream
326 end
327
328 def rpc_call(name, *values)
329 set_wiredump_file_base(name)
330 unless op_info = @operation[name]
331 raise RuntimeError, "method: #{name} not defined"
332 end
333 req_header = create_request_header
334 req_body = create_request_body(op_info, *values)
335 reqopt = create_options({
336 :soapaction => op_info.soapaction || @soapaction})
337 resopt = create_options({
338 :decode_typemap => @rpc_decode_typemap})
339 env = @proxy.route(req_header, req_body, reqopt, resopt)
340 raise EmptyResponseError unless env
341 receive_headers(env.header)
342 begin
343 @proxy.check_fault(env.body)
344 rescue ::SOAP::FaultError => e
345 Mapping.fault2exception(e)
346 end
347 ret = env.body.response ?
348 Mapping.soap2obj(env.body.response, @mapping_registry) : nil
349 if env.body.outparams
350 outparams = env.body.outparams.collect { |outparam|
351 Mapping.soap2obj(outparam)
352 }
353 return [ret].concat(outparams)
354 else
355 return ret
356 end
357 end
358
359 # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...]
360 # req_body: SOAPBasetype/SOAPCompoundtype
361 def document_send(name, header_obj, body_obj)
362 set_wiredump_file_base(name)
363 unless op_info = @operation[name]
364 raise RuntimeError, "method: #{name} not defined"
365 end
366 req_header = header_obj ? header_from_obj(header_obj, op_info) : nil
367 req_body = body_from_obj(body_obj, op_info)
368 opt = create_options({
369 :soapaction => op_info.soapaction || @soapaction,
370 :decode_typemap => @wsdl_types})
371 env = @proxy.invoke(req_header, req_body, opt)
372 raise EmptyResponseError unless env
373 if env.body.fault
374 raise ::SOAP::FaultError.new(env.body.fault)
375 end
376 res_body_obj = env.body.response ?
377 Mapping.soap2obj(env.body.response, @mapping_registry) : nil
378 return env.header, res_body_obj
379 end
380
381 private
382
383 def create_options(hash = nil)
384 opt = {}
385 opt[:default_encodingstyle] = @default_encodingstyle
386 opt[:allow_unqualified_element] = @allow_unqualified_element
387 opt[:generate_explicit_type] = @generate_explicit_type
388 opt.update(hash) if hash
389 opt
390 end
391
392 def set_wiredump_file_base(name)
393 if @wiredump_file_base
394 @proxy.set_wiredump_file_base(@wiredump_file_base + "_#{name}")
395 end
396 end
397
398 def create_request_header
399 headers = @proxy.headerhandler.on_outbound
400 if headers.empty?
401 nil
402 else
403 h = SOAPHeader.new
404 headers.each do |header|
405 h.add(header.elename.name, header)
406 end
407 h
408 end
409 end
410
411 def receive_headers(headers)
412 @proxy.headerhandler.on_inbound(headers) if headers
413 end
414
415 def create_request_body(op_info, *values)
416 method = create_method_struct(op_info, *values)
417 SOAPBody.new(method)
418 end
419
420 def create_method_struct(op_info, *params)
421 parts_names = op_info.bodyparts.collect { |part| part.name }
422 obj = create_method_obj(parts_names, params)
423 method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.op_name)
424 if method.members.size != parts_names.size
425 new_method = SOAPStruct.new
426 method.each do |key, value|
427 if parts_names.include?(key)
428 new_method.add(key, value)
429 end
430 end
431 method = new_method
432 end
433 method.elename = op_info.op_name
434 method.type = XSD::QName.new # Request should not be typed.
435 method
436 end
437
438 def create_method_obj(names, params)
439 o = Object.new
440 idx = 0
441 while idx < params.length
442 o.instance_variable_set('@' + names[idx], params[idx])
443 idx += 1
444 end
445 o
446 end
447
448 def header_from_obj(obj, op_info)
449 if obj.is_a?(SOAPHeader)
450 obj
451 elsif op_info.headerparts.empty?
452 if obj.nil?
453 nil
454 else
455 raise RuntimeError.new("no header definition in schema: #{obj}")
456 end
457 elsif op_info.headerparts.size == 1
458 part = op_info.headerparts[0]
459 header = SOAPHeader.new()
460 header.add(headeritem_from_obj(obj, part.element || part.eletype))
461 header
462 else
463 header = SOAPHeader.new()
464 op_info.headerparts.each do |part|
465 child = Mapping.get_attribute(obj, part.name)
466 ele = headeritem_from_obj(child, part.element || part.eletype)
467 header.add(part.name, ele)
468 end
469 header
470 end
471 end
472
473 def headeritem_from_obj(obj, name)
474 if obj.nil?
475 SOAPElement.new(name)
476 elsif obj.is_a?(SOAPHeaderItem)
477 obj
478 else
479 Mapping.obj2soap(obj, @doc_mapper, name)
480 end
481 end
482
483 def body_from_obj(obj, op_info)
484 if obj.is_a?(SOAPBody)
485 obj
486 elsif op_info.bodyparts.empty?
487 if obj.nil?
488 nil
489 else
490 raise RuntimeError.new("no body found in schema")
491 end
492 elsif op_info.bodyparts.size == 1
493 part = op_info.bodyparts[0]
494 ele = bodyitem_from_obj(obj, part.element || part.type)
495 SOAPBody.new(ele)
496 else
497 body = SOAPBody.new
498 op_info.bodyparts.each do |part|
499 child = Mapping.get_attribute(obj, part.name)
500 ele = bodyitem_from_obj(child, part.element || part.type)
501 body.add(ele.elename.name, ele)
502 end
503 body
504 end
505 end
506
507 def bodyitem_from_obj(obj, name)
508 if obj.nil?
509 SOAPElement.new(name)
510 elsif obj.is_a?(SOAPElement)
511 obj
512 else
513 Mapping.obj2soap(obj, @doc_mapper, name)
514 end
515 end
516
517 def add_method_interface(op_info)
518 name = XSD::CodeGen::GenSupport.safemethodname(op_info.op_name.name)
519 orgname = op_info.op_name.name
520 parts_names = op_info.bodyparts.collect { |part| part.name }
521 case op_info.style
522 when :document
523 if orgname != name and orgname.capitalize == name.capitalize
524 add_document_method_interface(orgname, parts_names)
525 end
526 add_document_method_interface(name, parts_names)
527 when :rpc
528 if orgname != name and orgname.capitalize == name.capitalize
529 add_rpc_method_interface(orgname, parts_names)
530 end
531 add_rpc_method_interface(name, parts_names)
532 else
533 raise RuntimeError.new("unknown style: #{op_info.style}")
534 end
535 end
536
537 def add_rpc_method_interface(name, parts_names)
538 ::SOAP::Mapping.define_singleton_method(@host, name) do |*arg|
539 unless arg.size == parts_names.size
540 raise ArgumentError.new(
541 "wrong number of arguments (#{arg.size} for #{parts_names.size})")
542 end
543 @servant.rpc_call(name, *arg)
544 end
545 @host.method(name)
546 end
547
548 def add_document_method_interface(name, parts_names)
549 ::SOAP::Mapping.define_singleton_method(@host, name) do |h, b|
550 @servant.document_send(name, h, b)
551 end
552 @host.method(name)
553 end
554
555 def setup_options
556 if opt = Property.loadproperty(::SOAP::PropertyName)
557 opt = opt["client"]
558 end
559 opt ||= Property.new
560 opt.add_hook("protocol.mandatorycharset") do |key, value|
561 @mandatorycharset = value
562 end
563 opt.add_hook("protocol.wiredump_file_base") do |key, value|
564 @wiredump_file_base = value
565 end
566 opt["protocol.http.charset"] ||= XSD::Charset.xml_encoding_label
567 opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
568 opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
569 opt
570 end
571 end
572end
573
574
575end
Note: See TracBrowser for help on using the repository browser.