| 1 |
# WSDL4R - WSDL additional definitions for SOAP. |
|---|
| 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 'wsdl/info' |
|---|
| 10 |
require 'xsd/namedelements' |
|---|
| 11 |
require 'soap/mapping' |
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
module WSDL |
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
class Definitions < Info |
|---|
| 18 |
def self.soap_rpc_complextypes |
|---|
| 19 |
types = XSD::NamedElements.new |
|---|
| 20 |
types << array_complextype |
|---|
| 21 |
types << fault_complextype |
|---|
| 22 |
types << exception_complextype |
|---|
| 23 |
types |
|---|
| 24 |
end |
|---|
| 25 |
|
|---|
| 26 |
def self.array_complextype |
|---|
| 27 |
type = XMLSchema::ComplexType.new(::SOAP::ValueArrayName) |
|---|
| 28 |
type.complexcontent = XMLSchema::ComplexContent.new |
|---|
| 29 |
type.complexcontent.restriction = XMLSchema::ComplexRestriction.new |
|---|
| 30 |
type.complexcontent.restriction.base = ::SOAP::ValueArrayName |
|---|
| 31 |
attr = XMLSchema::Attribute.new |
|---|
| 32 |
attr.ref = ::SOAP::AttrArrayTypeName |
|---|
| 33 |
anyarray = XSD::QName.new( |
|---|
| 34 |
XSD::AnyTypeName.namespace, |
|---|
| 35 |
XSD::AnyTypeName.name + '[]') |
|---|
| 36 |
attr.arytype = anyarray |
|---|
| 37 |
type.complexcontent.restriction.attributes << attr |
|---|
| 38 |
type |
|---|
| 39 |
end |
|---|
| 40 |
|
|---|
| 41 |
=begin |
|---|
| 42 |
<xs:complexType name="Fault" final="extension"> |
|---|
| 43 |
<xs:sequence> |
|---|
| 44 |
<xs:element name="faultcode" type="xs:QName" /> |
|---|
| 45 |
<xs:element name="faultstring" type="xs:string" /> |
|---|
| 46 |
<xs:element name="faultactor" type="xs:anyURI" minOccurs="0" /> |
|---|
| 47 |
<xs:element name="detail" type="tns:detail" minOccurs="0" /> |
|---|
| 48 |
</xs:sequence> |
|---|
| 49 |
</xs:complexType> |
|---|
| 50 |
=end |
|---|
| 51 |
def self.fault_complextype |
|---|
| 52 |
type = XMLSchema::ComplexType.new(::SOAP::EleFaultName) |
|---|
| 53 |
faultcode = XMLSchema::Element.new(::SOAP::EleFaultCodeName, XSD::XSDQName::Type) |
|---|
| 54 |
faultstring = XMLSchema::Element.new(::SOAP::EleFaultStringName, XSD::XSDString::Type) |
|---|
| 55 |
faultactor = XMLSchema::Element.new(::SOAP::EleFaultActorName, XSD::XSDAnyURI::Type) |
|---|
| 56 |
faultactor.minoccurs = 0 |
|---|
| 57 |
detail = XMLSchema::Element.new(::SOAP::EleFaultDetailName, XSD::AnyTypeName) |
|---|
| 58 |
detail.minoccurs = 0 |
|---|
| 59 |
type.all_elements = [faultcode, faultstring, faultactor, detail] |
|---|
| 60 |
type.final = 'extension' |
|---|
| 61 |
type |
|---|
| 62 |
end |
|---|
| 63 |
|
|---|
| 64 |
def self.exception_complextype |
|---|
| 65 |
type = XMLSchema::ComplexType.new(XSD::QName.new( |
|---|
| 66 |
::SOAP::Mapping::RubyCustomTypeNamespace, 'SOAPException')) |
|---|
| 67 |
excn_name = XMLSchema::Element.new(XSD::QName.new(nil, 'excn_type_name'), XSD::XSDString::Type) |
|---|
| 68 |
cause = XMLSchema::Element.new(XSD::QName.new(nil, 'cause'), XSD::AnyTypeName) |
|---|
| 69 |
backtrace = XMLSchema::Element.new(XSD::QName.new(nil, 'backtrace'), ::SOAP::ValueArrayName) |
|---|
| 70 |
message = XMLSchema::Element.new(XSD::QName.new(nil, 'message'), XSD::XSDString::Type) |
|---|
| 71 |
type.all_elements = [excn_name, cause, backtrace, message] |
|---|
| 72 |
type |
|---|
| 73 |
end |
|---|
| 74 |
|
|---|
| 75 |
def soap_rpc_complextypes(binding) |
|---|
| 76 |
types = rpc_operation_complextypes(binding) |
|---|
| 77 |
types + self.class.soap_rpc_complextypes |
|---|
| 78 |
end |
|---|
| 79 |
|
|---|
| 80 |
def collect_faulttypes |
|---|
| 81 |
result = [] |
|---|
| 82 |
collect_fault_messages.each do |name| |
|---|
| 83 |
faultparts = message(name).parts |
|---|
| 84 |
if faultparts.size != 1 |
|---|
| 85 |
raise RuntimeError.new("Expecting fault message \"#{name}\" to have ONE part") |
|---|
| 86 |
end |
|---|
| 87 |
fault_part = faultparts[0] |
|---|
| 88 |
# WS-I Basic Profile Version 1.1 (R2205) requires fault message parts |
|---|
| 89 |
# to refer to elements rather than types |
|---|
| 90 |
faulttype = fault_part.element |
|---|
| 91 |
if not faulttype |
|---|
| 92 |
warn("Fault message \"#{name}\" part \"#{fault_part.name}\" must specify an \"element\" attribute") |
|---|
| 93 |
faulttype = fault_part.type |
|---|
| 94 |
end |
|---|
| 95 |
if faulttype and result.index(faulttype).nil? |
|---|
| 96 |
result << faulttype |
|---|
| 97 |
end |
|---|
| 98 |
end |
|---|
| 99 |
result |
|---|
| 100 |
end |
|---|
| 101 |
|
|---|
| 102 |
private |
|---|
| 103 |
|
|---|
| 104 |
def get_fault_binding(op_binding, fault_name) |
|---|
| 105 |
op_binding.fault.each do |fault| |
|---|
| 106 |
return fault if fault.name == fault_name |
|---|
| 107 |
end |
|---|
| 108 |
return nil |
|---|
| 109 |
end |
|---|
| 110 |
|
|---|
| 111 |
def op_binding_declares_fault(op_binding, fault_name) |
|---|
| 112 |
return get_fault_binding(op_binding, fault_name) != nil |
|---|
| 113 |
end |
|---|
| 114 |
|
|---|
| 115 |
def collect_fault_messages |
|---|
| 116 |
result = [] |
|---|
| 117 |
porttypes.each do |porttype| |
|---|
| 118 |
port_binding = porttype.find_binding() |
|---|
| 119 |
next unless port_binding |
|---|
| 120 |
porttype.operations.each do |operation| |
|---|
| 121 |
op_binding = port_binding.operations.find { |ele| ele.name == operation.name } |
|---|
| 122 |
next unless op_binding |
|---|
| 123 |
operation.fault.each do |fault| |
|---|
| 124 |
# Make sure the operation fault has a name |
|---|
| 125 |
if not fault.name |
|---|
| 126 |
warn("Operation \"#{operation.name}\": fault must specify a \"name\" attribute") |
|---|
| 127 |
next |
|---|
| 128 |
end |
|---|
| 129 |
# Make sure that portType fault has a corresponding soap:fault |
|---|
| 130 |
# definition in binding section. |
|---|
| 131 |
if not op_binding_declares_fault(op_binding, fault.name) |
|---|
| 132 |
warn("Operation \"#{operation.name}\", fault \"#{fault.name}\": no corresponding wsdl:fault binding found with a matching \"name\" attribute") |
|---|
| 133 |
next |
|---|
| 134 |
end |
|---|
| 135 |
fault_binding = get_fault_binding(op_binding, fault.name) |
|---|
| 136 |
if fault_binding.soapfault.nil? |
|---|
| 137 |
warn("WARNING: no soap:fault found for wsdl:fault \"#{fault_binding.name}\" in operation \"#{operation.name}\" \n\n") |
|---|
| 138 |
next |
|---|
| 139 |
end |
|---|
| 140 |
if fault_binding.soapfault.name != fault_binding.name |
|---|
| 141 |
warn("WARNING: name of soap:fault \"#{fault_binding.soapfault.name}\" doesn't match the name of wsdl:fault \"#{fault_binding.name}\" in operation \"#{operation.name}\" \n\n") |
|---|
| 142 |
next |
|---|
| 143 |
end |
|---|
| 144 |
# According to WS-I (R2723): if in a wsdl:binding the use attribute |
|---|
| 145 |
# on a contained soapbind:fault element is present, its value MUST |
|---|
| 146 |
# be "literal". |
|---|
| 147 |
if fault_binding.soapfault.use and fault_binding.soapfault.use != "literal" |
|---|
| 148 |
warn("Operation \"#{operation.name}\", fault \"#{fault.name}\": soap:fault \"use\" attribute must be \"literal\"") |
|---|
| 149 |
end |
|---|
| 150 |
if result.index(fault.message).nil? |
|---|
| 151 |
result << fault.message |
|---|
| 152 |
end |
|---|
| 153 |
end |
|---|
| 154 |
end |
|---|
| 155 |
end |
|---|
| 156 |
result |
|---|
| 157 |
end |
|---|
| 158 |
|
|---|
| 159 |
def rpc_operation_complextypes(binding) |
|---|
| 160 |
types = XSD::NamedElements.new |
|---|
| 161 |
binding.operations.each do |op_bind| |
|---|
| 162 |
if op_bind_rpc?(op_bind) |
|---|
| 163 |
operation = op_bind.find_operation |
|---|
| 164 |
if op_bind.input |
|---|
| 165 |
type = XMLSchema::ComplexType.new(op_bind.soapoperation_name) |
|---|
| 166 |
message = messages[operation.input.message] |
|---|
| 167 |
type.sequence_elements = elements_from_message(message) |
|---|
| 168 |
types << type |
|---|
| 169 |
end |
|---|
| 170 |
if op_bind.output |
|---|
| 171 |
type = XMLSchema::ComplexType.new(operation.outputname) |
|---|
| 172 |
message = messages[operation.output.message] |
|---|
| 173 |
type.sequence_elements = elements_from_message(message) |
|---|
| 174 |
types << type |
|---|
| 175 |
end |
|---|
| 176 |
end |
|---|
| 177 |
end |
|---|
| 178 |
types |
|---|
| 179 |
end |
|---|
| 180 |
|
|---|
| 181 |
def op_bind_rpc?(op_bind) |
|---|
| 182 |
op_bind.soapoperation_style == :rpc |
|---|
| 183 |
end |
|---|
| 184 |
|
|---|
| 185 |
def elements_from_message(message) |
|---|
| 186 |
message.parts.collect { |part| |
|---|
| 187 |
if part.element |
|---|
| 188 |
collect_elements[part.element] |
|---|
| 189 |
elsif part.name.nil? or part.type.nil? |
|---|
| 190 |
raise RuntimeError.new("part of a message must be an element or typed") |
|---|
| 191 |
else |
|---|
| 192 |
qname = XSD::QName.new(nil, part.name) |
|---|
| 193 |
XMLSchema::Element.new(qname, part.type) |
|---|
| 194 |
end |
|---|
| 195 |
} |
|---|
| 196 |
end |
|---|
| 197 |
end |
|---|
| 198 |
|
|---|
| 199 |
|
|---|
| 200 |
end |
|---|