Class: Handsoap::Service

Inherits:
Object
  • Object
show all
Defined in:
lib/handsoap/service.rb,
lib/handsoap/service.rb

Constant Summary collapse

@@logger =
nil
@@instance =
{}

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object



318
319
320
321
322
323
324
325
# File 'lib/handsoap/service.rb', line 318

def method_missing(method, *args)
  action = self.class.get_mapping(method)
  if action
    invoke(action, *args)
  else
    super
  end
end

Class Method Details

.endpoint(args = {}) ⇒ Object

Sets the endpoint for the service. Arguments:

:uri                  => endpoint uri of the service. Required.
:version              => 1 | 2
:envelope_namespace   => Namespace of SOAP-envelope
:request_content_type => Content-Type of HTTP request.

You must supply either :version or both :envelope_namspace and :request_content_type. :version is simply a shortcut for default values.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/handsoap/service.rb', line 91

def self.endpoint(args = {})
  @uri = args[:uri] || raise("Missing option :uri")
  if args[:version]
    soap_namespace = { 1 => 'http://schemas.xmlsoap.org/soap/envelope/', 2 => 'http://www.w3.org/2003/05/soap-envelope' }
    raise("Unknown protocol version '#{@protocol_version.inspect}'") if soap_namespace[args[:version]].nil?
    @envelope_namespace = soap_namespace[args[:version]]
    @request_content_type = args[:version] == 1 ? "text/xml" : "application/soap+xml"
  end
  @envelope_namespace = args[:envelope_namespace] unless args[:envelope_namespace].nil?
  @request_content_type = args[:request_content_type] unless args[:request_content_type].nil?
  if @envelope_namespace.nil? || @request_content_type.nil?
    raise("Missing option :envelope_namespace, :request_content_type or :version")
  end
end

.envelope_namespaceObject



105
106
107
# File 'lib/handsoap/service.rb', line 105

def self.envelope_namespace
  @envelope_namespace
end

.fire_on_create_document(doc) ⇒ Object



332
333
334
335
336
# File 'lib/handsoap/service.rb', line 332

def self.fire_on_create_document(doc)
  if @create_document_callback
    @create_document_callback.call doc
  end
end

.get_mapping(name) ⇒ Object



315
316
317
# File 'lib/handsoap/service.rb', line 315

def self.get_mapping(name)
  @mapping[name] if @mapping
end

.instanceObject



115
116
117
# File 'lib/handsoap/service.rb', line 115

def self.instance
  @@instance[self.to_s] ||= self.new
end

.logger=(io) ⇒ Object



80
81
82
# File 'lib/handsoap/service.rb', line 80

def self.logger=(io)
  @@logger = io
end

.map_method(mapping) ⇒ Object

Registers a simple method mapping without any arguments and no parsing of response.

This is deprecated



309
310
311
312
313
314
# File 'lib/handsoap/service.rb', line 309

def self.map_method(mapping)
  if @mapping.nil?
    @mapping = {}
  end
  @mapping.merge! mapping
end

.method_missing(method, *args) ⇒ Object



118
119
120
121
122
123
124
# File 'lib/handsoap/service.rb', line 118

def self.method_missing(method, *args)
  if instance.respond_to?(method)
    instance.__send__ method, *args
  else
    super
  end
end

.on_create_document(&block) ⇒ Object

Registers a block to call when a request document is created.

This is deprecated, in favour of #on_create_document



329
330
331
# File 'lib/handsoap/service.rb', line 329

def self.on_create_document(&block)
  @create_document_callback = block
end

.request_content_typeObject



108
109
110
# File 'lib/handsoap/service.rb', line 108

def self.request_content_type
  @request_content_type
end

.uriObject



111
112
113
# File 'lib/handsoap/service.rb', line 111

def self.uri
  @uri
end

Instance Method Details

#debug(message = nil) ⇒ Object

:nodoc:



198
199
200
201
202
203
204
205
206
207
# File 'lib/handsoap/service.rb', line 198

def debug(message = nil) #:nodoc:
  if @@logger
    if message
      @@logger.puts(message)
    end
    if block_given?
      yield @@logger
    end
  end
end

#dispatch(doc, action) ⇒ Object

Send document and parses the response into a XmlQueryFront::XmlElement (XmlDocument)



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/handsoap/service.rb', line 229

def dispatch(doc, action)
  on_before_dispatch
  headers = {
    "Content-Type" => "#{self.class.request_content_type};charset=UTF-8"
  }
  headers["SOAPAction"] = action unless action.nil?
  response = send_http_request(self.class.uri, doc.to_s, headers)
  # Start the parsing pipe-line.
  # There are various stages and hooks for each, so that you can override those in your service classes.
  xml_document = parse_soap_response_document(response.primary_part.body)
  soap_fault = parse_soap_fault(xml_document)
  # Is the response a soap-fault?
  unless soap_fault.nil?
    return on_fault(soap_fault)
  end
  # Does the http-status indicate an error?
  if response.status >= 400
    return on_http_error(response)
  end
  # Does the response contain a valid xml-document?
  if xml_document.nil?
    return on_missing_document(response)
  end
  # Everything seems in order.
  on_response_document(xml_document)
  return SoapResponse.new(xml_document, response)
end

#invoke(action, options = { :soap_action => :auto }, &block) ⇒ Object

Creates an XML document and sends it over HTTP.

action is the QName of the rootnode of the envelope.

options currently takes one option :soap_action, which can be one of:

:auto sends a SOAPAction http header, deduced from the action name. (This is the default)

String sends a SOAPAction http header.

nil sends no SOAPAction http header.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/handsoap/service.rb', line 136

def invoke(action, options = { :soap_action => :auto }, &block) # :yields: Handsoap::XmlMason::Element
  if action
    if options.kind_of? String
      options = { :soap_action => options }
    end
    if options[:soap_action] == :auto
      options[:soap_action] = action.gsub(/^.+:/, "")
    elsif options[:soap_action] == :none
      options[:soap_action] = nil
    end
    doc = make_envelope do |body|
      body.add action
    end
    if block_given?
      yield doc.find(action)
    end
    dispatch(doc, options[:soap_action])
  end
end

#make_envelopeObject

Creates a standard SOAP envelope and yields the Body element.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/handsoap/service.rb', line 257

def make_envelope # :yields: Handsoap::XmlMason::Element
  doc = XmlMason::Document.new do |doc|
    doc.alias 'env', self.class.envelope_namespace
    doc.add "env:Envelope" do |env|
      env.add "*:Header"
      env.add "*:Body"
    end
  end
  self.class.fire_on_create_document doc # deprecated .. use instance method
  on_create_document(doc)
  if block_given?
    yield doc.find("Body")
  end
  return doc
end

#on_after_create_http_request(http_request) ⇒ Object

Hook that is called after the http_client is created.

You can override this to customize the http_client



168
169
# File 'lib/handsoap/service.rb', line 168

def on_after_create_http_request(http_request)
end

#on_before_dispatchObject

Hook that is called before the message is dispatched.

You can override this to provide filtering and logging.



163
164
# File 'lib/handsoap/service.rb', line 163

def on_before_dispatch
end

#on_create_document(doc) ⇒ Object

Hook that is called when a new request document is created.

You can override this to add namespaces and other elements that are common to all requests (Such as authentication).



158
159
# File 'lib/handsoap/service.rb', line 158

def on_create_document(doc)
end

#on_fault(fault) ⇒ Object

Hook that is called if the dispatch returns a Fault.

Default behaviour is to raise the Fault, but you can override this to provide logging and more fine-grained handling faults.

See also: parse_soap_fault



186
187
188
# File 'lib/handsoap/service.rb', line 186

def on_fault(fault)
  raise fault
end

#on_http_error(response) ⇒ Object

Hook that is called if there is a HTTP level error.

Default behaviour is to raise an error.

Raises:



178
179
180
# File 'lib/handsoap/service.rb', line 178

def on_http_error(response)
  raise HttpError, response
end

#on_missing_document(response) ⇒ Object

Hook that is called if the response does not contain a valid SOAP enevlope.

Default behaviour is to raise an error

Note that if your service has operations that are one-way, you shouldn’t raise an error here. This is however a fairly exotic case, so that is why the default behaviour is to raise an error.



195
196
197
# File 'lib/handsoap/service.rb', line 195

def on_missing_document(response)
  raise "The response is not a valid SOAP envelope"
end

#on_response_document(doc) ⇒ Object

Hook that is called when there is a response.

You can override this to register common namespaces, useful for parsing the document.



173
174
# File 'lib/handsoap/service.rb', line 173

def on_response_document(doc)
end

#parse_soap_fault(document) ⇒ Object

XmlDocument -> [Fault | nil]



281
282
283
284
285
286
# File 'lib/handsoap/service.rb', line 281

def parse_soap_fault(document)
  unless document.nil?
    node = document.xpath('/env:Envelope/env:Body/descendant-or-self::env:Fault', { 'env' => self.class.envelope_namespace }).first
    Fault.from_xml(node, :namespace => self.class.envelope_namespace) unless node.nil?
  end
end

#parse_soap_response_document(http_body) ⇒ Object

String -> [XmlDocument | nil]



273
274
275
276
277
278
279
# File 'lib/handsoap/service.rb', line 273

def parse_soap_response_document(http_body)
  begin
    Handsoap::XmlQueryFront.parse_string(http_body, Handsoap.xml_query_driver)
  rescue Handsoap::XmlQueryFront::ParseError => ex
    nil
  end
end

#send_http_request(uri, post_body, headers) ⇒ Object

Does the actual HTTP level interaction.



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/handsoap/service.rb', line 209

def send_http_request(uri, post_body, headers)
  request = Handsoap::Http::Request.new(uri, :post)
  headers.each do |key, value|
    request.add_header(key, value)
  end
  request.body = post_body
  debug do |logger|
    logger.puts request.inspect
  end
  on_after_create_http_request(request)
  http = Handsoap::Http.drivers[Handsoap.http_driver]
  response = http.send_http_request(request)
  debug do |logger|
    logger.puts(response.inspect do |body|
      Handsoap.pretty_format_envelope(body).chomp
    end)
  end
  return response
end