| 1 |
# SOAP4R - RPC Routing library |
|---|
| 2 |
# Copyright (C) 2000-2007 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. |
|---|
| 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 |
|
|---|
| 9 |
require 'soap/soap' |
|---|
| 10 |
require 'soap/processor' |
|---|
| 11 |
require 'soap/mapping' |
|---|
| 12 |
require 'soap/mapping/literalregistry' |
|---|
| 13 |
require 'soap/rpc/rpc' |
|---|
| 14 |
require 'soap/rpc/element' |
|---|
| 15 |
require 'soap/header/handlerset' |
|---|
| 16 |
require 'soap/filter' |
|---|
| 17 |
require 'soap/streamHandler' |
|---|
| 18 |
require 'soap/mimemessage' |
|---|
| 19 |
require 'soap/header/handlerset' |
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
module SOAP |
|---|
| 23 |
module RPC |
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
class Router |
|---|
| 27 |
include SOAP |
|---|
| 28 |
|
|---|
| 29 |
attr_reader :actor |
|---|
| 30 |
attr_accessor :mapping_registry |
|---|
| 31 |
attr_accessor :literal_mapping_registry |
|---|
| 32 |
attr_accessor :generate_explicit_type |
|---|
| 33 |
attr_accessor :use_default_namespace |
|---|
| 34 |
attr_accessor :external_ces |
|---|
| 35 |
attr_reader :filterchain |
|---|
| 36 |
|
|---|
| 37 |
def initialize(actor) |
|---|
| 38 |
@actor = actor |
|---|
| 39 |
@mapping_registry = nil |
|---|
| 40 |
@headerhandler = Header::HandlerSet.new |
|---|
| 41 |
@literal_mapping_registry = ::SOAP::Mapping::LiteralRegistry.new |
|---|
| 42 |
@generate_explicit_type = true |
|---|
| 43 |
@use_default_namespace = false |
|---|
| 44 |
@external_ces = nil |
|---|
| 45 |
@operation_by_soapaction = {} |
|---|
| 46 |
@operation_by_qname = {} |
|---|
| 47 |
@headerhandlerfactory = [] |
|---|
| 48 |
@filterchain = Filter::FilterChain.new |
|---|
| 49 |
end |
|---|
| 50 |
|
|---|
| 51 |
### |
|---|
| 52 |
## header handler interface |
|---|
| 53 |
# |
|---|
| 54 |
def add_request_headerhandler(factory) |
|---|
| 55 |
unless factory.respond_to?(:create) |
|---|
| 56 |
raise TypeError.new("factory must respond to 'create'") |
|---|
| 57 |
end |
|---|
| 58 |
@headerhandlerfactory << factory |
|---|
| 59 |
end |
|---|
| 60 |
|
|---|
| 61 |
def add_headerhandler(handler) |
|---|
| 62 |
@headerhandler.add(handler) |
|---|
| 63 |
end |
|---|
| 64 |
|
|---|
| 65 |
### |
|---|
| 66 |
## servant definition interface |
|---|
| 67 |
# |
|---|
| 68 |
def add_rpc_request_servant(factory, namespace) |
|---|
| 69 |
unless factory.respond_to?(:create) |
|---|
| 70 |
raise TypeError.new("factory must respond to 'create'") |
|---|
| 71 |
end |
|---|
| 72 |
obj = factory.create # a dummy instance for introspection |
|---|
| 73 |
::SOAP::RPC.defined_methods(obj).each do |name| |
|---|
| 74 |
begin |
|---|
| 75 |
qname = XSD::QName.new(namespace, name) |
|---|
| 76 |
param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name) |
|---|
| 77 |
opt = create_styleuse_option(:rpc, :encoded) |
|---|
| 78 |
add_rpc_request_operation(factory, qname, nil, name, param_def, opt) |
|---|
| 79 |
rescue SOAP::RPC::MethodDefinitionError => e |
|---|
| 80 |
p e if $DEBUG |
|---|
| 81 |
end |
|---|
| 82 |
end |
|---|
| 83 |
end |
|---|
| 84 |
|
|---|
| 85 |
def add_rpc_servant(obj, namespace) |
|---|
| 86 |
::SOAP::RPC.defined_methods(obj).each do |name| |
|---|
| 87 |
begin |
|---|
| 88 |
qname = XSD::QName.new(namespace, name) |
|---|
| 89 |
param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name) |
|---|
| 90 |
opt = create_styleuse_option(:rpc, :encoded) |
|---|
| 91 |
add_rpc_operation(obj, qname, nil, name, param_def, opt) |
|---|
| 92 |
rescue SOAP::RPC::MethodDefinitionError => e |
|---|
| 93 |
p e if $DEBUG |
|---|
| 94 |
end |
|---|
| 95 |
end |
|---|
| 96 |
end |
|---|
| 97 |
alias add_servant add_rpc_servant |
|---|
| 98 |
|
|---|
| 99 |
### |
|---|
| 100 |
## operation definition interface |
|---|
| 101 |
# |
|---|
| 102 |
def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {}) |
|---|
| 103 |
ensure_styleuse_option(opt, :rpc, :encoded) |
|---|
| 104 |
opt[:request_qname] = qname |
|---|
| 105 |
op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def, |
|---|
| 106 |
opt) |
|---|
| 107 |
if opt[:request_style] != :rpc |
|---|
| 108 |
raise RPCRoutingError.new("illegal request_style given") |
|---|
| 109 |
end |
|---|
| 110 |
assign_operation(soapaction, qname, op) |
|---|
| 111 |
end |
|---|
| 112 |
alias add_method add_rpc_operation |
|---|
| 113 |
alias add_rpc_method add_rpc_operation |
|---|
| 114 |
|
|---|
| 115 |
def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {}) |
|---|
| 116 |
ensure_styleuse_option(opt, :rpc, :encoded) |
|---|
| 117 |
opt[:request_qname] = qname |
|---|
| 118 |
op = RequestScopeOperation.new(soapaction, factory, name, param_def, opt) |
|---|
| 119 |
if opt[:request_style] != :rpc |
|---|
| 120 |
raise RPCRoutingError.new("illegal request_style given") |
|---|
| 121 |
end |
|---|
| 122 |
assign_operation(soapaction, qname, op) |
|---|
| 123 |
end |
|---|
| 124 |
|
|---|
| 125 |
def add_document_operation(receiver, soapaction, name, param_def, opt = {}) |
|---|
| 126 |
# |
|---|
| 127 |
# adopt workaround for doc/lit wrapper method |
|---|
| 128 |
# (you should consider to simply use rpc/lit service) |
|---|
| 129 |
# |
|---|
| 130 |
#unless soapaction |
|---|
| 131 |
# raise RPCRoutingError.new("soapaction is a must for document method") |
|---|
| 132 |
#end |
|---|
| 133 |
ensure_styleuse_option(opt, :document, :literal) |
|---|
| 134 |
op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def, |
|---|
| 135 |
opt) |
|---|
| 136 |
if opt[:request_style] != :document |
|---|
| 137 |
raise RPCRoutingError.new("illegal request_style given") |
|---|
| 138 |
end |
|---|
| 139 |
assign_operation(soapaction, first_input_part_qname(param_def), op) |
|---|
| 140 |
end |
|---|
| 141 |
alias add_document_method add_document_operation |
|---|
| 142 |
|
|---|
| 143 |
def add_document_request_operation(factory, soapaction, name, param_def, opt = {}) |
|---|
| 144 |
# |
|---|
| 145 |
# adopt workaround for doc/lit wrapper method |
|---|
| 146 |
# (you should consider to simply use rpc/lit service) |
|---|
| 147 |
# |
|---|
| 148 |
#unless soapaction |
|---|
| 149 |
# raise RPCRoutingError.new("soapaction is a must for document method") |
|---|
| 150 |
#end |
|---|
| 151 |
ensure_styleuse_option(opt, :document, :literal) |
|---|
| 152 |
op = RequestScopeOperation.new(soapaction, receiver, name, param_def, opt) |
|---|
| 153 |
if opt[:request_style] != :document |
|---|
| 154 |
raise RPCRoutingError.new("illegal request_style given") |
|---|
| 155 |
end |
|---|
| 156 |
assign_operation(soapaction, first_input_part_qname(param_def), op) |
|---|
| 157 |
end |
|---|
| 158 |
|
|---|
| 159 |
def route(conn_data) |
|---|
| 160 |
# we cannot set request_default_encodingsyle before parsing the content. |
|---|
| 161 |
env = unmarshal(conn_data) |
|---|
| 162 |
if env.nil? |
|---|
| 163 |
raise ArgumentError.new("illegal SOAP marshal format") |
|---|
| 164 |
end |
|---|
| 165 |
op = lookup_operation(conn_data.soapaction, env.body) |
|---|
| 166 |
headerhandler = @headerhandler.dup |
|---|
| 167 |
@headerhandlerfactory.each do |f| |
|---|
| 168 |
headerhandler.add(f.create) |
|---|
| 169 |
end |
|---|
| 170 |
soap_response = default_encodingstyle = nil |
|---|
| 171 |
begin |
|---|
| 172 |
receive_headers(headerhandler, env.header) |
|---|
| 173 |
soap_response = |
|---|
| 174 |
op.call(env.body, @mapping_registry, @literal_mapping_registry, |
|---|
| 175 |
create_mapping_opt) |
|---|
| 176 |
conn_data.is_fault = true if soap_response.is_a?(SOAPFault) |
|---|
| 177 |
default_encodingstyle = op.response_default_encodingstyle |
|---|
| 178 |
rescue Exception => e |
|---|
| 179 |
# If a wsdl fault was raised by service, the fault declaration details |
|---|
| 180 |
# is kept in wsdl_fault. Otherwise (exception is a program fault) |
|---|
| 181 |
# wsdl_fault is nil |
|---|
| 182 |
wsdl_fault_details = op.faults && op.faults[e.class.name] |
|---|
| 183 |
soap_response = fault(e, wsdl_fault_details) |
|---|
| 184 |
conn_data.is_fault = true |
|---|
| 185 |
default_encodingstyle = nil |
|---|
| 186 |
end |
|---|
| 187 |
header = call_headers(headerhandler) |
|---|
| 188 |
if op.response_use.nil? |
|---|
| 189 |
conn_data.send_string = '' |
|---|
| 190 |
conn_data.is_nocontent = true |
|---|
| 191 |
conn_data |
|---|
| 192 |
else |
|---|
| 193 |
body = SOAPBody.new(soap_response, conn_data.is_fault) |
|---|
| 194 |
env = SOAPEnvelope.new(header, body) |
|---|
| 195 |
marshal(conn_data, env, default_encodingstyle) |
|---|
| 196 |
end |
|---|
| 197 |
end |
|---|
| 198 |
|
|---|
| 199 |
# Create fault response string. |
|---|
| 200 |
def create_fault_response(e) |
|---|
| 201 |
env = SOAPEnvelope.new(SOAPHeader.new, SOAPBody.new(fault(e, nil), true)) |
|---|
| 202 |
opt = {} |
|---|
| 203 |
opt[:external_content] = nil |
|---|
| 204 |
@filterchain.reverse_each do |filter| |
|---|
| 205 |
env = filter.on_outbound(env, opt) |
|---|
| 206 |
break unless env |
|---|
| 207 |
end |
|---|
| 208 |
response_string = Processor.marshal(env, opt) |
|---|
| 209 |
conn_data = StreamHandler::ConnectionData.new(response_string) |
|---|
| 210 |
conn_data.is_fault = true |
|---|
| 211 |
if ext = opt[:external_content] |
|---|
| 212 |
mimeize(conn_data, ext) |
|---|
| 213 |
end |
|---|
| 214 |
conn_data |
|---|
| 215 |
end |
|---|
| 216 |
|
|---|
| 217 |
private |
|---|
| 218 |
|
|---|
| 219 |
def first_input_part_qname(param_def) |
|---|
| 220 |
param_def.each do |param| |
|---|
| 221 |
param = MethodDef.to_param(param) |
|---|
| 222 |
if param.io_type == SOAPMethod::IN |
|---|
| 223 |
return param.qname |
|---|
| 224 |
end |
|---|
| 225 |
end |
|---|
| 226 |
nil |
|---|
| 227 |
end |
|---|
| 228 |
|
|---|
| 229 |
def create_styleuse_option(style, use) |
|---|
| 230 |
opt = {} |
|---|
| 231 |
opt[:request_style] = opt[:response_style] = style |
|---|
| 232 |
opt[:request_use] = opt[:response_use] = use |
|---|
| 233 |
opt |
|---|
| 234 |
end |
|---|
| 235 |
|
|---|
| 236 |
def ensure_styleuse_option(opt, style, use) |
|---|
| 237 |
if opt[:request_style] || opt[:response_style] || opt[:request_use] || opt[:response_use] |
|---|
| 238 |
# do not edit |
|---|
| 239 |
else |
|---|
| 240 |
opt[:request_style] ||= style |
|---|
| 241 |
opt[:response_style] ||= style |
|---|
| 242 |
opt[:request_use] ||= use |
|---|
| 243 |
opt[:response_use] ||= use |
|---|
| 244 |
end |
|---|
| 245 |
end |
|---|
| 246 |
|
|---|
| 247 |
def assign_operation(soapaction, qname, op) |
|---|
| 248 |
assigned = false |
|---|
| 249 |
if soapaction and !soapaction.empty? |
|---|
| 250 |
@operation_by_soapaction[soapaction] = op |
|---|
| 251 |
assigned = true |
|---|
| 252 |
end |
|---|
| 253 |
if qname |
|---|
| 254 |
@operation_by_qname[qname] = op |
|---|
| 255 |
assigned = true |
|---|
| 256 |
end |
|---|
| 257 |
unless assigned |
|---|
| 258 |
raise RPCRoutingError.new("cannot assign operation") |
|---|
| 259 |
end |
|---|
| 260 |
end |
|---|
| 261 |
|
|---|
| 262 |
def lookup_operation(soapaction, body) |
|---|
| 263 |
if op = @operation_by_soapaction[soapaction] |
|---|
| 264 |
return op |
|---|
| 265 |
end |
|---|
| 266 |
qname = body.root_node.elename |
|---|
| 267 |
if op = @operation_by_qname[qname] |
|---|
| 268 |
return op |
|---|
| 269 |
end |
|---|
| 270 |
if soapaction |
|---|
| 271 |
raise RPCRoutingError.new( |
|---|
| 272 |
"operation: #{soapaction} #{qname} not supported") |
|---|
| 273 |
else |
|---|
| 274 |
raise RPCRoutingError.new("operation: #{qname} not supported") |
|---|
| 275 |
end |
|---|
| 276 |
end |
|---|
| 277 |
|
|---|
| 278 |
def call_headers(headerhandler) |
|---|
| 279 |
header = ::SOAP::SOAPHeader.new |
|---|
| 280 |
items = headerhandler.on_outbound(header) |
|---|
| 281 |
items.each do |item| |
|---|
| 282 |
header.add(item.elename.name, item) |
|---|
| 283 |
end |
|---|
| 284 |
header |
|---|
| 285 |
end |
|---|
| 286 |
|
|---|
| 287 |
def receive_headers(headerhandler, header) |
|---|
| 288 |
headerhandler.on_inbound(header) if header |
|---|
| 289 |
end |
|---|
| 290 |
|
|---|
| 291 |
def unmarshal(conn_data) |
|---|
| 292 |
xml = nil |
|---|
| 293 |
opt = {} |
|---|
| 294 |
contenttype = conn_data.receive_contenttype |
|---|
| 295 |
if /#{MIMEMessage::MultipartContentType}/i =~ contenttype |
|---|
| 296 |
opt[:external_content] = {} |
|---|
| 297 |
mime = MIMEMessage.parse("Content-Type: " + contenttype, |
|---|
| 298 |
conn_data.receive_string) |
|---|
| 299 |
mime.parts.each do |part| |
|---|
| 300 |
value = Attachment.new(part.content) |
|---|
| 301 |
value.contentid = part.contentid |
|---|
| 302 |
obj = SOAPAttachment.new(value) |
|---|
| 303 |
opt[:external_content][value.contentid] = obj if value.contentid |
|---|
| 304 |
end |
|---|
| 305 |
opt[:charset] = |
|---|
| 306 |
StreamHandler.parse_media_type(mime.root.headers['content-type'].str) |
|---|
| 307 |
xml = mime.root.content |
|---|
| 308 |
else |
|---|
| 309 |
opt[:charset] = ::SOAP::StreamHandler.parse_media_type(contenttype) |
|---|
| 310 |
xml = conn_data.receive_string |
|---|
| 311 |
end |
|---|
| 312 |
@filterchain.each do |filter| |
|---|
| 313 |
xml = filter.on_inbound(xml, opt) |
|---|
| 314 |
break unless xml |
|---|
| 315 |
end |
|---|
| 316 |
env = Processor.unmarshal(xml, opt) |
|---|
| 317 |
charset = opt[:charset] |
|---|
| 318 |
conn_data.send_contenttype = "text/xml; charset=\"#{charset}\"" |
|---|
| 319 |
env |
|---|
| 320 |
end |
|---|
| 321 |
|
|---|
| 322 |
def marshal(conn_data, env, default_encodingstyle = nil) |
|---|
| 323 |
opt = {} |
|---|
| 324 |
opt[:external_content] = nil |
|---|
| 325 |
opt[:default_encodingstyle] = default_encodingstyle |
|---|
| 326 |
opt[:generate_explicit_type] = @generate_explicit_type |
|---|
| 327 |
opt[:use_default_namespace] = @use_default_namespace |
|---|
| 328 |
@filterchain.reverse_each do |filter| |
|---|
| 329 |
env = filter.on_outbound(env, opt) |
|---|
| 330 |
break unless env |
|---|
| 331 |
end |
|---|
| 332 |
response_string = Processor.marshal(env, opt) |
|---|
| 333 |
conn_data.send_string = response_string |
|---|
| 334 |
if ext = opt[:external_content] |
|---|
| 335 |
mimeize(conn_data, ext) |
|---|
| 336 |
end |
|---|
| 337 |
conn_data |
|---|
| 338 |
end |
|---|
| 339 |
|
|---|
| 340 |
def mimeize(conn_data, ext) |
|---|
| 341 |
mime = MIMEMessage.new |
|---|
| 342 |
ext.each do |k, v| |
|---|
| 343 |
mime.add_attachment(v.data) |
|---|
| 344 |
end |
|---|
| 345 |
mime.add_part(conn_data.send_string + "\r\n") |
|---|
| 346 |
mime.close |
|---|
| 347 |
conn_data.send_string = mime.content_str |
|---|
| 348 |
conn_data.send_contenttype = mime.headers['content-type'].str |
|---|
| 349 |
conn_data |
|---|
| 350 |
end |
|---|
| 351 |
|
|---|
| 352 |
# Create fault response. |
|---|
| 353 |
def fault(e, wsdl_fault_details) |
|---|
| 354 |
if e.is_a?(UnhandledMustUnderstandHeaderError) |
|---|
| 355 |
faultcode = FaultCode::MustUnderstand |
|---|
| 356 |
else |
|---|
| 357 |
faultcode = FaultCode::Server |
|---|
| 358 |
end |
|---|
| 359 |
|
|---|
| 360 |
# If the exception represents a WSDL fault, the fault element should |
|---|
| 361 |
# be added as the SOAP fault <detail> element. If the exception is a |
|---|
| 362 |
# normal program exception, it is wrapped inside a custom SOAP4R |
|---|
| 363 |
# SOAP exception element. |
|---|
| 364 |
detail = nil |
|---|
| 365 |
begin |
|---|
| 366 |
if (wsdl_fault_details) |
|---|
| 367 |
registry = wsdl_fault_details[:use] == "literal" ? |
|---|
| 368 |
@literal_mapping_registry : @mapping_registry |
|---|
| 369 |
faultQName = XSD::QName.new( |
|---|
| 370 |
wsdl_fault_details[:ns], wsdl_fault_details[:name] |
|---|
| 371 |
) |
|---|
| 372 |
detail = Mapping.obj2soap(e, registry, faultQName) |
|---|
| 373 |
# wrap fault element (SOAPFault swallows top-level element) |
|---|
| 374 |
wrapper = SOAP::SOAPElement.new(faultQName) |
|---|
| 375 |
wrapper.add(detail) |
|---|
| 376 |
detail = wrapper |
|---|
| 377 |
else |
|---|
| 378 |
# Exception is a normal program exception. Wrap it. |
|---|
| 379 |
detail = Mapping.obj2soap(Mapping::SOAPException.new(e), |
|---|
| 380 |
@mapping_registry) |
|---|
| 381 |
detail.elename ||= XSD::QName::EMPTY # for literal mappingregstry |
|---|
| 382 |
end |
|---|
| 383 |
rescue |
|---|
| 384 |
detail = SOAPString.new("failed to serialize detail object: #{$!}") |
|---|
| 385 |
end |
|---|
| 386 |
|
|---|
| 387 |
SOAPFault.new( |
|---|
| 388 |
SOAPElement.new(nil, faultcode), |
|---|
| 389 |
SOAPString.new(e.to_s), |
|---|
| 390 |
SOAPString.new(@actor), |
|---|
| 391 |
detail) |
|---|
| 392 |
end |
|---|
| 393 |
|
|---|
| 394 |
def create_mapping_opt |
|---|
| 395 |
{ :external_ces => @external_ces } |
|---|
| 396 |
end |
|---|
| 397 |
|
|---|
| 398 |
class Operation |
|---|
| 399 |
attr_reader :name |
|---|
| 400 |
attr_reader :soapaction |
|---|
| 401 |
attr_reader :request_style |
|---|
| 402 |
attr_reader :response_style |
|---|
| 403 |
attr_reader :request_use |
|---|
| 404 |
attr_reader :response_use |
|---|
| 405 |
attr_reader :faults |
|---|
| 406 |
|
|---|
| 407 |
def initialize(soapaction, name, param_def, opt) |
|---|
| 408 |
@soapaction = soapaction |
|---|
| 409 |
@name = name |
|---|
| 410 |
@request_style = opt[:request_style] |
|---|
| 411 |
@response_style = opt[:response_style] |
|---|
| 412 |
@request_use = opt[:request_use] |
|---|
| 413 |
@response_use = opt[:response_use] |
|---|
| 414 |
@faults = opt[:faults] |
|---|
| 415 |
check_style(@request_style) |
|---|
| 416 |
check_style(@response_style) |
|---|
| 417 |
check_use(@request_use) |
|---|
| 418 |
check_use(@response_use) |
|---|
| 419 |
if @response_style == :rpc |
|---|
| 420 |
request_qname = opt[:request_qname] or raise |
|---|
| 421 |
@rpc_method_factory = |
|---|
| 422 |
RPC::SOAPMethodRequest.new(request_qname, param_def, @soapaction) |
|---|
| 423 |
@rpc_response_qname = opt[:response_qname] |
|---|
| 424 |
else |
|---|
| 425 |
@doc_request_qnames = [] |
|---|
| 426 |
@doc_response_qnames = [] |
|---|
| 427 |
param_def.each do |param| |
|---|
| 428 |
param = MethodDef.to_param(param) |
|---|
| 429 |
case param.io_type |
|---|
| 430 |
when SOAPMethod::IN |
|---|
| 431 |
@doc_request_qnames << param.qname |
|---|
| 432 |
when SOAPMethod::OUT |
|---|
| 433 |
@doc_response_qnames << param.qname |
|---|
| 434 |
else |
|---|
| 435 |
raise ArgumentError.new( |
|---|
| 436 |
"illegal inout definition for document style: #{param.io_type}") |
|---|
| 437 |
end |
|---|
| 438 |
end |
|---|
| 439 |
end |
|---|
| 440 |
end |
|---|
| 441 |
|
|---|
| 442 |
def request_default_encodingstyle |
|---|
| 443 |
(@request_use == :encoded) ? EncodingNamespace : LiteralNamespace |
|---|
| 444 |
end |
|---|
| 445 |
|
|---|
| 446 |
def response_default_encodingstyle |
|---|
| 447 |
(@response_use == :encoded) ? EncodingNamespace : LiteralNamespace |
|---|
| 448 |
end |
|---|
| 449 |
|
|---|
| 450 |
def call(body, mapping_registry, literal_mapping_registry, opt) |
|---|
| 451 |
if @request_style == :rpc |
|---|
| 452 |
values = request_rpc(body, mapping_registry, literal_mapping_registry, |
|---|
| 453 |
opt) |
|---|
| 454 |
else |
|---|
| 455 |
values = request_document(body, mapping_registry, |
|---|
| 456 |
literal_mapping_registry, opt) |
|---|
| 457 |
end |
|---|
| 458 |
result = receiver.method(@name.intern).call(*values) |
|---|
| 459 |
return result if result.is_a?(SOAPFault) |
|---|
| 460 |
if @response_style == :rpc |
|---|
| 461 |
response_rpc(result, mapping_registry, literal_mapping_registry, opt) |
|---|
| 462 |
elsif @doc_response_qnames.empty? |
|---|
| 463 |
# nothing to do |
|---|
| 464 |
else |
|---|
| 465 |
response_doc(result, mapping_registry, literal_mapping_registry, opt) |
|---|
| 466 |
end |
|---|
| 467 |
end |
|---|
| 468 |
|
|---|
| 469 |
private |
|---|
| 470 |
|
|---|
| 471 |
def receiver |
|---|
| 472 |
raise NotImplementedError.new('must be defined in derived class') |
|---|
| 473 |
end |
|---|
| 474 |
|
|---|
| 475 |
def request_rpc(body, mapping_registry, literal_mapping_registry, opt) |
|---|
| 476 |
request = body.request |
|---|
| 477 |
unless request.is_a?(SOAPNameAccessible) |
|---|
| 478 |
if request.is_a?(SOAPNil) |
|---|
| 479 |
# SOAP::Lite/0.69 seems to send xsi:nil="true" element as a request. |
|---|
| 480 |
request = SOAPStruct.new(request.elename) |
|---|
| 481 |
else |
|---|
| 482 |
raise RPCRoutingError.new("not an RPC style") |
|---|
| 483 |
end |
|---|
| 484 |
end |
|---|
| 485 |
if @request_use == :encoded |
|---|
| 486 |
request_rpc_enc(request, mapping_registry, opt) |
|---|
| 487 |
else |
|---|
| 488 |
request_rpc_lit(request, literal_mapping_registry, opt) |
|---|
| 489 |
end |
|---|
| 490 |
end |
|---|
| 491 |
|
|---|
| 492 |
def request_document(body, mapping_registry, literal_mapping_registry, opt) |
|---|
| 493 |
# ToDo: compare names with @doc_request_qnames |
|---|
| 494 |
if @request_use == :encoded |
|---|
| 495 |
request_doc_enc(body, mapping_registry, opt) |
|---|
| 496 |
else |
|---|
| 497 |
request_doc_lit(body, literal_mapping_registry, opt) |
|---|
| 498 |
end |
|---|
| 499 |
end |
|---|
| 500 |
|
|---|
| 501 |
def request_rpc_enc(request, mapping_registry, opt) |
|---|
| 502 |
param = Mapping.soap2obj(request, mapping_registry, nil, opt) |
|---|
| 503 |
request.collect { |key, value| |
|---|
| 504 |
param[key] |
|---|
| 505 |
} |
|---|
| 506 |
end |
|---|
| 507 |
|
|---|
| 508 |
def request_rpc_lit(request, mapping_registry, opt) |
|---|
| 509 |
request.collect { |key, value| |
|---|
| 510 |
Mapping.soap2obj(value, mapping_registry, nil, opt) |
|---|
| 511 |
} |
|---|
| 512 |
end |
|---|
| 513 |
|
|---|
| 514 |
def request_doc_enc(body, mapping_registry, opt) |
|---|
| 515 |
body.collect { |key, value| |
|---|
| 516 |
Mapping.soap2obj(value, mapping_registry, nil, opt) |
|---|
| 517 |
} |
|---|
| 518 |
end |
|---|
| 519 |
|
|---|
| 520 |
def request_doc_lit(body, mapping_registry, opt) |
|---|
| 521 |
body.collect { |key, value| |
|---|
| 522 |
Mapping.soap2obj(value, mapping_registry, nil, opt) |
|---|
| 523 |
} |
|---|
| 524 |
end |
|---|
| 525 |
|
|---|
| 526 |
def response_rpc(result, mapping_registry, literal_mapping_registry, opt) |
|---|
| 527 |
if @response_use == :encoded |
|---|
| 528 |
response_rpc_enc(result, mapping_registry, opt) |
|---|
| 529 |
else |
|---|
| 530 |
response_rpc_lit(result, literal_mapping_registry, opt) |
|---|
| 531 |
end |
|---|
| 532 |
end |
|---|
| 533 |
|
|---|
| 534 |
def response_doc(result, mapping_registry, literal_mapping_registry, opt) |
|---|
| 535 |
if @doc_response_qnames.size == 0 |
|---|
| 536 |
result = [] |
|---|
| 537 |
elsif @doc_response_qnames.size == 1 |
|---|
| 538 |
result = [result] |
|---|
| 539 |
end |
|---|
| 540 |
if result.size != @doc_response_qnames.size |
|---|
| 541 |
raise "required #{@doc_response_qnames.size} responses " + |
|---|
| 542 |
"but #{result.size} given" |
|---|
| 543 |
end |
|---|
| 544 |
if @response_use == :encoded |
|---|
| 545 |
response_doc_enc(result, mapping_registry, opt) |
|---|
| 546 |
else |
|---|
| 547 |
response_doc_lit(result, literal_mapping_registry, opt) |
|---|
| 548 |
end |
|---|
| 549 |
end |
|---|
| 550 |
|
|---|
| 551 |
def response_rpc_enc(result, mapping_registry, opt) |
|---|
| 552 |
soap_response = |
|---|
| 553 |
@rpc_method_factory.create_method_response(@rpc_response_qname) |
|---|
| 554 |
if soap_response.have_outparam? |
|---|
| 555 |
unless result.is_a?(Array) |
|---|
| 556 |
raise RPCRoutingError.new("out parameter was not returned") |
|---|
| 557 |
end |
|---|
| 558 |
outparams = {} |
|---|
| 559 |
i = 1 |
|---|
| 560 |
soap_response.output_params.each do |outparam| |
|---|
| 561 |
outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry, |
|---|
| 562 |
nil, opt) |
|---|
| 563 |
i += 1 |
|---|
| 564 |
end |
|---|
| 565 |
soap_response.set_outparam(outparams) |
|---|
| 566 |
soap_response.retval = Mapping.obj2soap(result[0], mapping_registry, |
|---|
| 567 |
nil, opt) |
|---|
| 568 |
else |
|---|
| 569 |
soap_response.retval = Mapping.obj2soap(result, mapping_registry, nil, |
|---|
| 570 |
opt) |
|---|
| 571 |
end |
|---|
| 572 |
soap_response |
|---|
| 573 |
end |
|---|
| 574 |
|
|---|
| 575 |
def response_rpc_lit(result, mapping_registry, opt) |
|---|
| 576 |
soap_response = |
|---|
| 577 |
@rpc_method_factory.create_method_response(@rpc_response_qname) |
|---|
| 578 |
if soap_response.have_outparam? |
|---|
| 579 |
unless result.is_a?(Array) |
|---|
| 580 |
raise RPCRoutingError.new("out parameter was not returned") |
|---|
| 581 |
end |
|---|
| 582 |
outparams = {} |
|---|
| 583 |
i = 1 |
|---|
| 584 |
soap_response.output_params.each do |outparam| |
|---|
| 585 |
outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry, |
|---|
| 586 |
XSD::QName.new(nil, outparam), opt) |
|---|
| 587 |
i += 1 |
|---|
| 588 |
end |
|---|
| 589 |
soap_response.set_outparam(outparams) |
|---|
| 590 |
soap_response.retval = Mapping.obj2soap(result[0], mapping_registry, |
|---|
| 591 |
soap_response.elename, opt) |
|---|
| 592 |
else |
|---|
| 593 |
soap_response.retval = Mapping.obj2soap(result, mapping_registry, |
|---|
| 594 |
soap_response.elename, opt) |
|---|
| 595 |
end |
|---|
| 596 |
soap_response |
|---|
| 597 |
end |
|---|
| 598 |
|
|---|
| 599 |
def response_doc_enc(result, mapping_registry, opt) |
|---|
| 600 |
(0...result.size).collect { |idx| |
|---|
| 601 |
ele = Mapping.obj2soap(result[idx], mapping_registry, nil, opt) |
|---|
| 602 |
ele.elename = @doc_response_qnames[idx] |
|---|
| 603 |
ele |
|---|
| 604 |
} |
|---|
| 605 |
end |
|---|
| 606 |
|
|---|
| 607 |
def response_doc_lit(result, mapping_registry, opt) |
|---|
| 608 |
(0...result.size).collect { |idx| |
|---|
| 609 |
ele = Mapping.obj2soap(result[idx], mapping_registry, |
|---|
| 610 |
@doc_response_qnames[idx]) |
|---|
| 611 |
ele.encodingstyle = LiteralNamespace |
|---|
| 612 |
ele |
|---|
| 613 |
} |
|---|
| 614 |
end |
|---|
| 615 |
|
|---|
| 616 |
def check_style(style) |
|---|
| 617 |
unless [:rpc, :document].include?(style) |
|---|
| 618 |
raise ArgumentError.new("unknown style: #{style}") |
|---|
| 619 |
end |
|---|
| 620 |
end |
|---|
| 621 |
|
|---|
| 622 |
# nil means oneway |
|---|
| 623 |
def check_use(use) |
|---|
| 624 |
unless [:encoded, :literal, nil].include?(use) |
|---|
| 625 |
raise ArgumentError.new("unknown use: #{use}") |
|---|
| 626 |
end |
|---|
| 627 |
end |
|---|
| 628 |
end |
|---|
| 629 |
|
|---|
| 630 |
class ApplicationScopeOperation < Operation |
|---|
| 631 |
def initialize(soapaction, receiver, name, param_def, opt) |
|---|
| 632 |
super(soapaction, name, param_def, opt) |
|---|
| 633 |
@receiver = receiver |
|---|
| 634 |
end |
|---|
| 635 |
|
|---|
| 636 |
private |
|---|
| 637 |
|
|---|
| 638 |
def receiver |
|---|
| 639 |
@receiver |
|---|
| 640 |
end |
|---|
| 641 |
end |
|---|
| 642 |
|
|---|
| 643 |
class RequestScopeOperation < Operation |
|---|
| 644 |
def initialize(soapaction, receiver_factory, name, param_def, opt) |
|---|
| 645 |
super(soapaction, name, param_def, opt) |
|---|
| 646 |
unless receiver_factory.respond_to?(:create) |
|---|
| 647 |
raise TypeError.new("factory must respond to 'create'") |
|---|
| 648 |
end |
|---|
| 649 |
@receiver_factory = receiver_factory |
|---|
| 650 |
end |
|---|
| 651 |
|
|---|
| 652 |
private |
|---|
| 653 |
|
|---|
| 654 |
def receiver |
|---|
| 655 |
@receiver_factory.create |
|---|
| 656 |
end |
|---|
| 657 |
end |
|---|
| 658 |
end |
|---|
| 659 |
|
|---|
| 660 |
|
|---|
| 661 |
end |
|---|
| 662 |
end |
|---|