Archive for the 'Guttershark' Category
Removing Classes from MXMLC Link Reports
I started creating swcs for guttershark and ran into an annoying problem that I’m sure others have run into. In order to exclude classes from a swc you have to generate a link report from mxmlc. Then re-create the swc but tell mxmlc to load that report file and use it as the source for files to exclude.
That’s great and all, but the link report is usually huge, especially if you’re code links against flash components or flvplayback code. So in order to include only the classes you want (like only guttershark classes), I was starting to poke through the link report, and remove the guttershark classes.
Just to get this straight. MXMLC uses the link report as a source for actionscript classes to exclude from the swc. So if I want only the guttershark classes, you have to go into the link report and remove any guttershark class. The GS classes won’t be in the link report, which will cause them to be included in the swc.
Pretty simple; either poke through it and remove classes manually or try to write regular expressions to match only GS files. Then you think, “shit, this is a huge pain in the ass, and I’m going to waste a ton of time every time I need to update this link report.”
Here’s what I’m getting to: a python script to do it for you. This is the general usage sequence:
#...generate swc and link report with compc... python swc_link_report_remove.py -l report.xml -m "/Users/aaronsmith/Development/git/guttershark" -o gsreport.xml #...generate swc passing gsreport.xml for -load-externs to compc...
Here’s what the script does:
- Load the original report.xml and parse it
- Loop through each script item
- If the script name attribute matches against the -m property, skip the item.
- Write out the new link report file.
This is a much faster way to remove the classes you actually want included in the swc. I don’t use mxmlc or compc all that much, so maybe there is any easier way but the documentation certainly doesn’t outline any.
I’m on a mac, and you’ll need to install “lxml” for python.
sudo easy_install lxmlHere’s the script. Put it somewhere in your path for easy access and make sure it’s executable. Even if this isn’t exactly what you need, it’s a good starting point for scripting updates to the link reports.
No commentsGuttershark Updates
I finished the service layer updates. I ended up re-writing the flash remoting classes, and mostly re-writing the soap classes. There are a bunch more examples in the repository for working with http, remoting, and soap. It’s really good stuff and simplifies those layers even more than what’s in the AS3 api.
You may also notice one other update and it may or may not piss you off; I re-organized the package structure. All the examples have been updated. And I’ve been combing through the documentation again making updates. Mostly nit picky things. And I’ve been taking out examples from the documentation in favor of putting a note that states the examples are in the repository. It’s easier to maintain those examples, rather than hard coded examples in the source code.
2 commentsGuttershark Service Abstraction Updates
I’m working on revamping the service abstractions in Guttershark. I’ll be completely removing the ServiceManager in favor of using a class directly (like HTTPCall, SoapService, etc). I’ve got the http stuff done, and need to update soap and remoting. The http code is the most drastically changed. The soap and remoting code won’t change too much.
I’ve got a couple examples in the repo for the new HTTPCall class. But in short, here’s how you use it:
package { import gs.core.DocumentController; import gs.service.http.HTTPCall; import gs.service.http.HTTPCallResponseFormat; import gs.service.http.HTTPCallResult; public class Main extends DocumentController { private var hc:HTTPCall; public function Main() { super(); } override protected function setupComplete():void { hc=new HTTPCall("http://www.google.com/"); hc.responseFormat=HTTPCallResponseFormat.TEXT; hc.setCallbacks(onResult); hc.send(); } protected function onResult(r:HTTPCallResult):void { trace(r.text); } } }
And if you look through the docs, it supports a bunch of other stuff - retries, timeouts, more response formats than URLLoaderResponseFormat, and you can optionally set callbacks instead of using .addEventListener. Which is a nice alternative that allows you to setup the handlers you need in one line of code. Of course you can still opt in to using .addEventListener.
3 commentsNew TextAttributes Class in Guttershark
I added this new class called TextAttributes. It simplifies setting the right properties on a text field for text formats, stylesheets, a few other things, and the model has helpers to simplify it even more.
Here’s a model xml file:
<?xml version="1.0" encoding="utf-8"?> <model> <assets> <asset libraryName="fonts" source="fonts.swf" preload="true" /> </assets> <fonts> <font libraryName="LucidaGrandeBold" /> <font libraryName="LucidaGrandeRegular" /> </fonts> <stylesheets> <stylesheet id="style1"> <![CDATA[ .body { color:#000000; font:LucidaGrandeBold; } .test { color:#ff0066; } ]]> </stylesheet> </stylesheets> <textAttributes> <attribute id="attr1" styleSheetId='style1' wrapInBodySpan="true" antiAliasType="advanced" /> </textAttributes> </model>
Simple enough, the model defines fonts, stylesheets, and now the additional textAttributes can be defined.
Here’s a guttershark document controller showing the use of text attributes.
package { import gs.core.DocumentController; import gs.core.Preloader; import flash.events.Event; import flash.text.TextField; public class Main extends DocumentController { public var t1:TextField; public var t2:TextField; override protected function flashvarsForStandalone():Object { return {model:"model.xml"}; } override protected function startPreload():void { preloader=new Preloader(); preloader.addEventListener(Event.COMPLETE,onPreloadComplete); preloader.addItems(model.getAssetsForPreload()); preloader.start(); } override protected function onPreloadComplete(e:Event):void { super.onPreloadComplete(e); model.getTextAttributeById("attr1").apply(t1,t2); t1.htmlText = "<span class='body'>hello <span class='test'>world</span></span>"; } } }
The Model has a new method called getTextAttributeById which will give you an instance of a TextAttributes class, which coincidentally contains a method to apply it to any number of text fields.
The TextAttributes class can be used by itself, but having a helper method on the Model just simplifies defining all that information in XML.
There’s another example in the guttershark repo.
No commentsTracking With Guttershark
I thought I’d take a minute to show how I implement tracking with guttershark. I’ve used this a lot and it always works. I wrote this tracking manager a while ago with the intentions of figuring out these problems:
- Provide some sort of assertions mechanism so the manager will first assert some condition, then fire or not fire.
- Fire different information based on if the assertion is true or false.
- Provide dynamic data in combination with tracking information from xml. Like the index of an image let’s say.
First thing’s first, here’s an example xml snippet:
<?xml version="1.0" encoding="utf-8"?> <tracking> <track id="tag1"> <webtrends>tag1,tag2,tag3</webtrends> </track> <track id="tag2"> <webtrends>tag1,tag2,tag3</webtrends> </track> </tracking>
Currently the tracking manager only supports webtrends and hitbox. I haven’t written any projects that require anything else yet. There are hooks in the tracking manager for ganalytics, omniture, and atlas; so anyone else could implement it and share it with me to be put in GS.
The tracking manager will fire whatever tracking type you have defined in a track node. Here’s an example xml file with both webtrends and hitbox:
<?xml version="1.0" encoding="utf-8"?> <tracking> <track id="tag1"> <webtrends>tag1,tag2,tag3</webtrends> <hitbox> <lpos>asdf</lpos> <lid>ghjk</lid> </hitbox> </track> <track id="tag2"> <webtrends>tag1,tag2,tag3</webtrends> </track> </tracking>
So the tracking node “tag1″ would fire both webtrends and hitbox. This also shows that depending on the tracking type, the tracking manager will parse the xml differently.
Let’s take the webtrends tag for example. The tracking manager will split the content on a comma (”,”), and use index 0 as “dcsuri”, index 1 as “ti”, and index 2 as “cg_n”. Those are webtrends variables passed to their dcsMultiTrack function.
So let’s take a look at getting this to fire. Here’s a document class that would do it:
package { import gs.core.DocumentController; import gs.core.Preloader; import gs.util.MathUtils; import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class Main extends DocumentController { public var clip:MovieClip; override protected function flashvarsForStandalone():Object { //the model file contains the tracking.xml file asset return {model:"model.xml"}; } override protected function onModelReady():void { preloader=new Preloader(); //preloader will load the tracking.xml asset preloader.addItems(model.getAssetsForPreload()); preloader.addEventListener(Event.COMPLETE,onPreloadComplete); preloader.start(); } override protected function initTracking():void { super.initTracking(); //tracking is initialized in DocumentController //tell the tracking manager to fire the "tag1" track when clip is clicked. tracking.register(clip,MouseEvent.CLICK,"tag1"); } } }
Because this is using the guttershark framework there’s one hidden thing. Guttershark will load a model.xml file which contains our tracking.xml file asset.
Here’s what that model.xml file looks like:
<?xml version="1.0" encoding="utf-8"?> <model> <assets> <asset libraryName="tracking" src="tracking.xml" preload="true" /> </assets> </model>
All the model xml does is define a tracking asset which is an xml file. The guttershark document controller will find that asset, and initialize a tracking manager for us. It’s actually really straightforward. All the DocumentController does is expose hooks and sets up some default things that I use all the time. Read the source for DocumentController for more info. Anyway, back to the tracking.
With everything that we’ve setup so far. A tracking call would be fired for “tag1″ when you click on the movie clip. This is where it starts to get interesting.
Let’s say we didn’t want the tracking to fire if clip is disabled. Here’s how you can do that:
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertProp:"enabled" }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } ...
The tracking manager internally will end up using “if(clip["enabled"]) fire()”. The tracking manager can also use a method:
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertMethod:shouldFire }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } private function shouldFire():Boolean { return (clip.alpha > .25) ? true : false; } ...
With that update, the tracking event would only fire if the clip’s alpha is greater than .25.
That covers some of the basic assertions.
What if we wanted to choose which tag to fire depending on if the assertion is true or false? First let’s make an update to the xml:
<?xml version="1.0" encoding="utf-8"?> <tracking> ... <track id="clipWasShown"> <webtrends>tag1,tag2,tag3</webtrends> </track> <track id="clipWasHidden"> <webtrends>tag1,tag2,tag3</webtrends> </track> ... </tracking>
And here’s how I’d update the actionscript to accommodate this.
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertProp:"visible", whenTrue:"clipWasShown", whenFalse:"clipWasHidden" }; tracking.register(clip,MouseEvent.CLICK,null,options); } ...
That’s really just another useful option in the tracking manager arsenal.
Now for the last problem, dynamic data. Let’s say we wanted to include the index of some slideshow, which indicates which picture their looking at. And let’s say we want it appended to the “cg_n” variable that get’s sent to webtrends.
Here’s how you can implement dynamic data:
... private var index:int = 0; private function next():void { index++; render(); } private function prev():void { index--; render(); } override protected function initTracking():void { super.initTracking(); var options:Object = { dynamicData:slideShowIndex }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } private function slideShowIndex():Array { return [null,null,index]; } ...
So in this case, the tracking manager expects an array for the “dynamic data” for webtrends. And will append whatever is in the array to the tags from xml before firing.
The dynamic data just happens to be implemented this way for webtrends. But if it’s a different tracking type you would return whatever the tracking manager is expecting. So for example, the “hitbox” tracking would require a function like this:
private function slideShowIndex():Object { return {lpos:index}; }
That’s pretty much it. As mentioned, I don’t have omniture, ganalytics, or atlas implemented, but there are hooks in the tracking manager. So if anyone out there needs to implement then please share.
I like using the tracking manager this way because it limits the amount of code I have to write to actually fire tracking. And keeps the real tracking tags out of your code.
There’s one other thing to mention - whenever you’re working with someone from an analytics department who is creating the tags for you. Make sure they know to always put dynamic data at the end of the tags. It’s easier to implement ;)
There are a couple examples in the guttershark repository.
1 commentNew KeyHandler Class
I’ve added a new class to replace the KeyManager. The key manager was nice but somewhat complicated internally. I’m replacing it with this new key handler class - It’s similar to the key manager except that it will only handle one key event per instance VS the key manager which would manage every key event you wanted.
I added a new feature called auto target, which will enable, or disable the key handler when the target is removed or added to the stage.
Here’s how you use it:
package { import gs.util.KeyHandler; import gs.util.StageRef; import flash.display.MovieClip; import flash.display.Sprite; import flash.utils.setTimeout; public class Main extends Sprite { public var mc:MovieClip; private var kh:KeyHandler; private var kh2:KeyHandler; private var kh3:KeyHandler; private var kh4:KeyHandler; public function Main():void { //the stage ref needs to be set somewhere StageRef.stage=stage; //create key handlers. kh=new KeyHandler("SHIFT+M",onShiftM); kh2=new KeyHandler("UP",onUp); kh3=new KeyHandler("f",onLetter); kh4=new KeyHandler("as",onSequence); //auto target example (enabled/disabled depending on if the target is on the stage) //the "kh" keyHandler won't work when the clip is off the stage. kh.autoTarget=mc; setTimeout(removeChild,2000,mc); setTimeout(addChild,4000,mc); } private function onSequence():void { trace("sequence as"); } private function onLetter():void { trace("letter f"); } private function onShiftM():void { trace("SHIFT+M"); } private function onUp():void { trace("UP"); } } }
There’s a functioning example in the guttershark repo.
2 commentsMore Guttershark Updates
Recently I put up some updates to guttershark - they sucked, so I’ve got another update. I was getting tired of having to include 90K for any swf that uses GS. So I went through and decoupled quite a bit.
Primarily it was all the singletons that were included. Originally, when I wrote the first version of GS I used all the singletons for performance and speed, but for how often some of those utilities are used it really doesn’t matter. And if you use FDT or Flex Builder, importing everything you need will be auto-completed anyways. So who cares!
The first version of GS included a couple classes called CoreSprite, and CoreClip - they included properties so that you’d always have access to a bunch of stuff, like a Model for instance. Instead of that, your class should create it’s own model, save it, and import it in any class that uses it.
You can still create your own base classes that contain properties you use all the time, but I’m not going to tell you what you need and what you don’t need; this is one of the reasons why GS would include everything if you subclassed either CoreSprite or CoreClip. So for any project you have, write your own base classes that included references to the things you need; that’s what I do.
Here’s a contrived example.
class Main { var model:Model; function Main() { model = new Model(); Model.set("main",model); } } class AnotherClass { private var model:Model; public function AnotherClass { model = Model.get("main"); } }
There are a number other classes that have this type of functionality on it. You can find them in the docs. Anyway, I’m much happier with this. Generally a shell swf will end up being around 35K.
There are a crap ton of examples in the repository. Make sure to check those out, you might see how awesome GS is and how much time it’ll save you.
If you wrote anything with GS in V1 or V2, you won’t be able to swap it out for GS3 without making some updates. The most notable things I changed:
- Took out CoreClip and CoreSprite. There’s GSClip or GSSprite now, but only fixes null stage references.
- Most utility classes have static methods on them instead of singletons.
- AssetManager is static
- Re-ordered method parameters on the LayoutManager to use …rest style parameters
- There’s a new DocumentController class which is simpler.
- Added a new TextAttributes class (gs.utils.TextAttributes). And added new model functionality to simplify setting text attributes on text fields. Like stylesheets, textformats, etc. Now it’s a one liner to set text attributes.
Additionally, I’ve updated the repositories on my gitweb page. The guttershark repo is the latest version, and I’ve updated the docs. You won’t need to look for gs2 docs or the gs2 repo anymore.
1 commentGuttershark Updates
It’s been a while since I’ve made any significant updates to guttershark - I’ve got some in the pipeline.
The most significant problem with guttershark has been that it’s really hard to create more than one guttershark application. Loading them in the same swf was not straightforward because of the Model class being a singleton.
I’ve also taken out the DocumentController and replaced it with an App class that is somewhat similar, but simplified.
Here’s an example of how you create a document class with these updates:
package { import gs.core.*; import gs.display.*; import gs.support.events.*; public class Main extends CoreSprite { override protected function initApp():void { app=new App("default",stage,utils.player.getFlashvars(this,flashvarsForStandalone)); app.addEventListener(AppEvent.MODEL_LOADED,onModelReady); app.loadModel(app.flashvars.model); } protected function flashvarsForStandalone():Object { return {model:"model.xml"}; } private function onModelReady(e:AppEvent):void { app.removeEventListener(AppEvent.MODEL_LOADED,onModelReady); //everything's ready to kick off the site } } }
There are only a few classes that you should look through; which should indicate the changes coming.
- gs.core.App
- gs.display.CoreClip
- gs.display.CoreSprite
I also changed guttershark to use a newer release of TweenMax (the com.greensock package).
I’m waiting to commit the new version until I get the examples in the repo updated - I’m about half way through. Until then, here’s a zip file of the new version that you can snoop through.
2 commentsGuttershark 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 commentsGuttershark Service Manager: HTTP Calls
A key thing in flash that has to be done all too often is making some sort of service call. If you’re not using an AS3 api for a social service, like (cough twitter), it tends to be annoying to implement - because you always write the same code to accomplish some http call, but it’s just sending different parameters.
Guttershark has a service manager that handles doing all this for you, with built-in options for retries, timeouts, maximum attempts, and a bunch of other stuff. And it’s all integrated with the Model, or you can use it by itself. Let’s take a look.
Manually setting up the service manager:
package { import net.guttershark.managers.ServiceManager; import net.guttershark.support.servicemanager.shared.CallResult; public class Main extends Sprite { private var sm:ServiceManager; public function Main() { sm = ServiceManager.gi(); sm.createHTTPService("searchGoogle","http://www.google.com/",3,5000); var data:Object = {}; data.q = "Guttershark"; sm.searchGoogle({ method:"get", data:data, responseFormat:"text", onResult:onGoogleResult, onFault:onGoogleFault, onRetry:onGoogleRetry, onTimeout:onGoogleTimedOut, onFirstCall:onGoogleFirstCall, }); } public function onGoogleResult(cr:CallResult):void { trace(cr.result); hideStatusIndicator(); } public function onGoogleFault(cf:CallFault):void { trace("FAULT"); hideStatusIndicator(); } public function onGoogleFirstCall():void { trace("first call to google service."); statusIndicator.show(); } public function onGoogleRetry():void { trace("retrying google service"); } public function onGoogleTimedOut():void { trace("timedout"); hideStatusIndicator(); } private function hideStatusIndicator():void { statusIndicator.hide(); } } }
Quick side note, please bare with me - some of this is a mix of psuedo code just to illustrate how the callbacks can be used to control different elements.
The above code is somewhat obvious, except for one line:
sm.createHTTPService("searchGoogle","http://www.google.com/",3,5000);
This creates a service called “searchGoogle”, who’s endpoint is that URL.. 3 is the number of attempts to allow, and 5000 is the timeout for each attempt. So in this case, it will call the service once, if a result or fault doesn’t happen within 5000 milliseconds, another attempt will be fired, so on and so fourth.
The service manager, like a couple other classes in Guttershark is a dynamic classes. So, when you create the http service, you can access it like a property of the service manager (sm.searchGoogle).
Easy though right? Let’s look at how it’s integrated with the model:
Model XML:
<?xml version="1.0" encoding="utf-8"> <model> <services> <service id="searchGoogle" url="http://www.google.com/" attempts="3" timeout="5000" /> <services> </model>
Here’s an update Main class file that extends doc controller to integrate the model:
package { import net.guttershark.control.DocumentController; import net.guttershark.managers.ServiceManager; import net.guttershark.support.servicemanager.shared.CallResult; public class Main extends DocumentController { override protected function flashvarsForStandalone():Object { return { model:"model.xml", initServices:true } } override protected function setupComplete():void { var data:Object = {}; data.q = "Guttershark"; sm.searchGoogle({ method:"get", data:data, responseFormat:"text", onResult:onGoogleResult, onFault:onGoogleFault, onRetry:onGoogleRetry, onTimeout:onGoogleTimedOut, onFirstCall:onGoogleFirstCall, }); } public function onGoogleResult(cr:CallResult):void { trace(cr.result); hideStatusIndicator(); } public function onGoogleFault(cf:CallFault):void { trace("FAULT"); hideStatusIndicator(); } public function onGoogleFirstCall():void { trace("first call to google service."); statusIndicator.show(); } public function onGoogleRetry():void { trace("retrying google service"); } public function onGoogleTimedOut():void { trace("timedout"); hideStatusIndicator(); } private function hideStatusIndicator():void { statusIndicator.hide(); } } }
Response Formats
The service manager, handles a few different types of response formats: text, binary, xml, and variables. The service manager will intelligently handle results, based off of those types.
For example, let’s say you make a service call to get some XML data. Let’s assume the request completed with a 200 OK. But, there was an error on the server side that caused a result to be returned that isn’t the expected data. As long as structure the XML a certain way, the service manager will call you onFault callback. Here’s an example:
XML Response Format
The service call:
sm.createHTTPService("myXMLService","http://example.com/xmlService",3,5000); sm.myXMLService({method:"post",responseFormat:"xml",...});
Successful result (will call the onResult callback):
<?xml version="1.0" encoding="utf-8"> <response> <someData>Hello World</someData> </response>
Trigger a fault:
<?xml version="1.0" encoding="utf-8"> <response> <fault>There was an error</fault> </response>
Variables Response Format
The service call:
sm.createHTTPService("myXMLService","http://example.com/xmlService",3,5000); sm.myXMLService({method:"post",responseFormat:"variables",...});
Successful result (will call the onResult callback):
&name=Aaron&lname=Smith&site=codeendeavor.com
Trigger a fault:
&fault=There was an error
You can access the result variables as an object on the CallResult, for example:
function onResult(cr:CallResult):void { trace(cr.result.lname); }
The only response formats that are intelligently handled are variables and xml. The other formats are raw data so you’d have to handle it yourself in the result callback.
Routes with Web Frameworks
The service manager can also handle routes for http services. The general ideas is that the “service” is the base URL to the site that exposes routes, then you tell the service manager which routes to append. For example:
sm.createHTTPService("myRestfulSite","http://www.example.com/",3,5000); var username:String = "b00g3r"; sm.myRestfulService({method:"GET", routes:["user",username,"friends"], responseFormat:"xml"});
This example will end up calling “http://www.example.com/user/b00g3r/friends”.
Conclusion
That’s all there is for HTTP services with the service manager. The service manager also exposes remoting, and SOAP - in the same dynamic/simplistic manner.
I would recommend reading the source that supports HTTP services:
net.guttershark.managers.ServiceManager; net.guttershark.support.servicemanager.shared.BaseCall net.guttershark.support.servicemanager.shared.CallResult net.guttershark.support.servicemanager.shared.CallFault net.guttershark.support.servicemanager.http.Service net.guttershark.support.servicemanager.http.ServiceCall
I’ll show you the remoting, and SOAP support next.
No comments