SOAP, ugh.

Obviously, as a Rails developer, I would love it if all web services offered a REST API. It would make my life easier and drastically reduce the development curve. Sadly, though, the majority of the services I’ve had to integrate with lately only offer SOAP.

Thankfully, there’s soap4r. Hiroshi Nakamura took on the largely thankless job of making all of our lives easier, but creating a gem that implements SOAP for Ruby.

Still, as admirably as it accomplishes it’s goal, soap4r is apparently not perfect. While working through my most recent integration, I came up against a couple of issues, that don’t seem like they would be unique, so I thought I’d pass the problems and solutions along.

Here are the problems:

  1. The API requires the inclusion, in the SOAP header, of a session id. Include it in the body, or don’t include it at all, and you’ll get “Invalid session” errors.
  2. The session expires, so that session id has to be changed.

As good as SOAP4r is, it seems it’s not well equipped for these requirements (or, quite possibly, my knowledge of its usage is deficient in these areas). Without going into detail about how I tracked the issues down, I offer you the following, my solutions:

1. This first problem seemed simple enough. The API WSDL offered a SessionHeader object, so I just assign the session id value and then use the SOAP driver’s headerhandler to include it, right?

Wrong.

Everything seems fine at first, no errors get raised on assignation. But, wait! When you actually try to execute a method via the SOAP driver, tragedy strikes:

NoMethodError: undefined method `on_outbound_headeritem’

There’s a whole stack trace that follows, leading deep into the bowels of the soap4r gem. Probably, the right thing to do would be to dig in to the code and fix it, submit a patch, yadda yadda. And, really, I’ll get to that (for both of these issues), I will. But when this came up, I needed a solution, and I needed it fast.

Here’s what I did:

  1. I used wsdl2ruby to pull down the API WSDL and convert it into Ruby friendly classes.
  2. In the SessionHeader class, I added the on_outbound_headeritem method (your implementation will vary, depending on the definitions in the MappingRegistry file):
  def on_outbound_headeritem(test)
    sobj = SOAP::SOAPElement.new(XSD::QName.new("ns1", 'SessionHeader'))
    sobj.add(SOAP::SOAPElement.new(XSD::QName.new("ns1", "session"), @session))
    ::SOAP::SOAPHeaderItem.new(sobj, false)
  end

The “test” parameter in the method prototype is just a garbage variable, because soap4r expected the method to accept something. The rest is basically the translation of the SessionHeader class into a SOAP::Element, with @session being the one and only class attribute.

2. The above solution works beautifully, except for one thing. The headerhandler in soap4r doesn’t allow for the clearing or overwriting of objects in the headerhandler. So, what happens is that after a day or so of active use, you end up with a half a dozen disparate session ids floating around in the header. I did some surfing and found this article: http://emphaticsolutions.com/2008/05/06/soap-headers-per-request-in-ruby.html, which details a solution. Basically, the problem is easily fixed by opening up the HandlerSet object and adding a couple of methods.

And so, a little monkey patching in the environment.rb:

  class SOAP::Header::HandlerSet
    def reset
      @store = XSD::NamedElements.new
    end
 
    def set(header)
      reset
      add header
    end
  end

Now, all you have to do is substitute this code:

driver.headerhandler << session_header

For this:

driver.headerhandler.set session_header

Ta Da!

That pretty much covers things, I think. Leave a comment or email me if you have any questions.

2 com

So, this was a pain: I spent about 2 hours this weekend trying to find an easy way to execute a SQL select statement while circumventing the ActiveRecord model. After scouring the web and some old code, I found this:

Model.connection.select_rows(sql)

It returns an array of arrays, each inner array representing a record of data. Easy to do, and the results are easy to deal with.

one

Categories

Links