AARON SMITH’S CODE ENDEAVOR

Guttershark Service Manager: SOAP

Ok folks, here’s how you can hook up soap with guttershark.

Here’s a main class: (there are a couple things I explain next)

package
{
 
	public class Main extends Sprite
	{
 
		private var sm:ServiceManager;
 
		public function Main()
		{
			sm=ServiceManager.gi();
			sm.createSOAPService("myWSDL","http://www.example.com/wsdl",3,5000);
			callSoap();
		}
 
		public function callSoap()
		{
			sm.myWSDL.someMethod({
				params:{someParameter:"hello world"},
				resultHandlerClass:MySoapResultHandler,
				onResult:onSoapResult,
				onFault:onSoapFault,
				onFirstCall:onFirstCall,
				onMethodNotAvailable:onMethodNotAvailable,
			});
		}
 
		public function onFirstCall():void
		{
			trace("trying soap service");
			showLoader();
		}
 
		public function onSoapFault(sf:SoapFault):void
		{
			trace("fault");
		}
 
		public function onSoapResult(sr:SoapResult):void
		{
			trace("result");
			trace(sr.raw);
			trace(sr.handler); //instance of resultHandlerClass.
		}
 
		public function onMethodNotAvailable():void
		{
			trace("method was not available on wsdl");
		}
	}
}

When you create a soap service (createSOAPService), the service manager downloads your WSDL and parses it out. It stores some information that you can get at runtime. For example, if you wanted to list out the supported methods on this wsdl:

sm.myWSDL.listMethods();

That’s generally all that you need, and that’s only needed in support of debugging.

Generating the Soap Message

The service manager generates the soap message to send to the server based off of the params object you give it. In the above code example, it’s going to generate a soap message that looks like this:

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <someMethod xmlns="http://webservicelocation/">
      <someParameter>hello world</someParameter>
    </someMethod>
  </soap:Body>
</soap:Envelope>

Handling the Result

The service manager requires you to give it a “result handler class.” This handler class gives you a hook to handle the soap result, before it’s handed off to your callbacks.

In the above example, there is this line (part of call props):

resultHandlerClass:MySoapResultHandler,

“MySoapResultHandler” needs to be a class that you create, to handle the result. Here’s that class:

package
{
 
	import net.guttershark.support.servicemanager.soap.SoapFaultError;
	import net.guttershark.support.servicemanager.soap.SoapResultHandler;
 
	public class MySoapResultHandler extends SoapResultHandler
	{
 
		public var somePropertyOnTheResult;
 
		override protected function process():void
		{
			super.process();
			super.setNamespace();
			super.setBody();
			super.setFault();
 
			//if it get's passed this its an ok result.
			if(soapFault) throw new SoapFaultError(soapFault);
 
			somePropertyOnTheResult = soapBody.someResult.toString();
		}
	}
}

The reason we create this soap result handler class is to delegate processing of the result XML into this class, and abstract it out of your application and the service manager.

There’s one thing to note that’s special about the SoapResultHandler, if you throw a “SoapFaultErorr”, anywhere from the process method, it will be raised back up through the service manager, and trigger your “onFault” callback.

So, in the above handler class, if “soapFault” is set, it throws an error - as mentioned this will ultimately trigger your onFault callback.

Now let’s take a look at an updated “onResult” callback function from the Main class, to do something with our handler:

public function onSoapResult(sr:SoapResult):void
{
    trace("result");
    trace(sr.raw); //raw soap xml response.
    trace(sr.handler); //this is the instance of your result handler - an instance of MySoapResultHandler.
    trace(sr.handler.somePropertyOnTheResult);
}

After the service manager processes your soap result handler, it calls your onResult handler. There’s a property on the SoapResult class called “handler”, which will be an instance of your handler class.

The service manager uses your handler class as a middle man so you can do what you want with the result - before it’s passed on. Hopefully you can see how nice this is. It keeps everything abstract from your application, and allows you to just focus on business logic. And also not clutter the methods that do all the work with a result.

Generally, for every soap method you’re going to call, you create a soap handler class. Each soap handler class can process the result specifically for that method call.

Other things to note

There’s one additional property you can set on a service call “showSoapRequest”:

sm.myWSDL.someMethod({
  params:{someParameter:"hello world"},
  resultHandlerClass:MySoapResultHandler,
  onResult:onSoapResult,
  onFault:onSoapFault,
  onFirstCall:onFirstCall,
  onMethodNotAvailable:onMethodNotAvailable,
  showSoapRequest:true, //
});

This option will trace the generated soap request that’s being sent to the server.

Integrating Soap With The Model

Here’s a model file:

<xml version="1.0" encoding="utf-8">
<model>
	<services>
		<service id="mySoapService" wsdl="mySoapServiceWSDL" attempts="3" timeout="5000" />
		<wsdl id="mySoapServiceWSDL" endpoint="http://www.example.com/wsdl" />
	</services>
</model>

And here’s the update class file:

package
{
 
	import net.guttershark.control.DocumentController;
 
	public class Main extends DocumentController
	{
 
		override protected function flashvarsForStandalone():Object
		{
			return {
				model:"model.xml",
				initServices:true,
			}
		}
 
		override protected function setupComplete():void
		{
			callSoap();
		}
 
		public function callSoap()
		{
			sm.myWSDL.someMethod({
				params:{someParameter:"hello world"},
				resultHandlerClass:MySoapResultHandler,
				onResult:onSoapResult,
				onFault:onSoapFault,
				onFirstCall:onFirstCall,
				onMethodNotAvailable:onMethodNotAvailable,
				showSoapRequest:true,
			});
		}
 
		public function onFirstCall():void
		{
			trace("trying soap service");
			showLoader();
		}
 
		public function onSoapFault(sf:SoapFault):void
		{
			trace("fault");
		}
 
		public function onSoapResult(sr:SoapResult):void
		{
			trace("result");
			trace(sr.raw);
			trace(sr.handler); //instance of resultHandlerClass.
			trace(sr.handler.somePropertyOnTheResult);
		}
 
		public function onMethodNotAvailable():void
		{
			trace("method was not available on wsdl");
		}
	}
}

Crossdomain Issues

With SOAP, Flash has to send over a header called “SOAPAction”, so you’re crossdomains need to allow headers for this.

Conclusion

Soap services are greatly simplified with guttershark - which allows you to focus on business logic of you’re application. Additionally, I hate wasting time on code that’s already been written, don’t you?

Additionally, you should read through the source of the classes that support SOAP functionality. They are in “net.guttershark.support.servicemanager.soap.”

  

6 Comments so far

  1. Francis November 30th, 2009 7:06 pm

    This looks great! But I’m slightly troubled by the fact i can’t seem to find your source. I’m sure I’ve over looked it somewhere but if you could kindly point me in the direction to where your source is located I’d be very happy.

    Keep up the good work - will keep reading this blog coz there’s a whole bunch of good things that I need to brush up on.

    Thanx :).

  2. admin November 30th, 2009 10:38 pm

    @Francis

    Yeah checkout my gitweb page: http://gitweb.codeendeavor.com/. You’re looking for guttershark. clone it with: “git clone git://git.codeendeavor.com/guttershark.git”

    Enjoy!

  3. admin November 30th, 2009 10:42 pm

    also the GS docs are here:
    http://codeendeavor.com/gsdocs

  4. Francis December 1st, 2009 9:59 am

    thanx for the help :)

  5. Nicholas Shipes March 4th, 2010 9:36 am

    Thanks for the awesome framework and constant enhancements, have loved the ease of use and quick development with it!

    Quick question… I’m trying to setup some remoting services with AMFPHP which are defined in the model using the same format as outlined in the model.xml example in the docs. However, when I try to access the services from within my application after the model has been loaded it states that the service is not available (via ServiceManager.serviceExist). The docs reference examples/services/ directory, but not seeing these examples. Are they the same as those that appear here on the blog? Do you have a working example of initializing services from the external model.xml?

    Thanks!

  6. Nicholas Shipes March 4th, 2010 9:54 am

    ^^Disregard my question, I figured it out. I forgot to use ServiceManager.set() to store my instance for reference later in my application (which was realized after several cups of coffee).

Leave a reply