3. Remoting
Context & Remote Components
The Context object is the central part of Tide. It is the client-side representation of the server contexts.
It is stored in the Tide singleton instance and is thus accessible from anywhere in the Flex application. There are subclasses of the Tide class depending on the server technology (Seam, Spring, EJB3, CDI).
import org.granite.tide.seam.Context;
var tideContext:Context = Seam.getInstance().getSeamContext();
import org.granite.tide.spring.Context;
var tideContext:Context = Spring.getInstance().getSpringContext();
import org.granite.tide.ejb.Context;
var tideContext:Context = Ejb.getInstance().getEjbContext();
import org.granite.tide.cdi.Context;
var tideContext:Context = Cdi.getInstance().getCdiContext();
By default, the Tide instance is wired to a destination named seam, spring, ejb or cdi. Optionally, it is possible to initialize it with another destination with getInstance("mydestination"). Only the very first call to getInstance("myDestination") will determine the destination. All following calls to getInstance() will use the previously defined destination whatever argument is passed.
The server components can then be referenced by their name as properties of the Context. For example, getting tideContext.helloWorld returns a proxy to a server component named helloWorld.
To call a remote method, you can call any method on this proxy object and it will be processed as a remote call and sent to the server with its parameters.
tideContext.helloWorld.sayHello("Jimi", helloResult, helloFault);
The helloResult and helloFault are both optional and define response handlers for success and fault cases.
The previous line will result in a remote call on the method sayHello of the helloWorld component with the String parameter "Jimi":
private function helloResult(event:TideResultEvent):void { Alert.show(event.result); }
The method result is then provided in the result property of the TideResultEvent.
Of course, all objects that can be serialized by GDS are supported, even lazy loaded objects or collections.
Errors can be handled by providing a faultHandler:
private function helloFault(event:TideFaultEvent):void { Alert.show(event.fault.toString()); }
The Fault object represents the remote exception thrown on the server.
Property accessors on the client proxy are not translated to remote calls. If you need to call a remote getter, you can just do (with the parenthesis notation):
tideContext.myComponent.getMyProperty(myPropertyResult);
but not
tideContext.myComponent.myProperty = "value";
var value:Object = tideContext.myComponent.myProperty;
This second option will not trigger remote calls, rather just set and get the current local value of myProperty. In the case of Seam and CDI, values set locally on the component are synchronized with the server component on the next remote call. For Spring and EJB3, doing this will have absolutely no effect.
Using the ITideResponder Interface
In some cases, you may need to pass some value to the result/fault handler to be able to distinguish different calls on the same method. You can then implement the ITideResponder interface or use the default TideResponder implementation that can hold a token object:
public function call():void { var responder1:TideResponder = new TideResponder(helloResult, helloFault, "firstCall"); var responder2:TideResponder = new TideResponder(helloResult, helloFault, "secondCall"); tideContext.helloWorld.sayHello("Jimi", responder1); tideContext.helloWorld.sayHello("Jimi", responder2); } private function helloResult(event:TideResultEvent, token:Object):void { if (token == "firstCall") Alert.show(event.result); }
In this case, the Alert will show up only once for the first call.
Simplifying asynchronous interactions
The ITideResponder interface has another important use : it make possible to provide a return object that will be merged with the server result. It greatly helps working with the asynchronous nature of Flex remoting :
private var products:ArrayCollection = new ArrayCollection(); public function call():void { tideContext.productService.findAllProducts( new TideResponder(resultHandler, null, null, products) ); } private function resultHandler(event:TideResultEvent):void { trace("Assert result was merged: " + (event.result === products)); }
<mx:DataGrid dataProvider="{products}"> ... </mx:DataGrid>
The result of the remote call will be merged in the provided products collection instance. It is thus mandatory to provide a not null object instance, and this kind of merge will not work with simple types (such as String, Number, ...).
Service initializer
Tide remoting can be used without needing the standard services-config.xml Flex configuration file. In this case, it is necessary to manually define the remoting channels. As Tide encapsulates the use of RemoteObject, it also provides a way of customizing these channels. The easiest way is to setup the built-in default DefaultServiceInitializer implementation in the Tide context :
Tide.getInstance().addComponentWithFactory("serviceInitializer", DefaultServiceInitializer,
{ contextRoot: "/context-root" }
);
It is also possible to define serverName, serverPort and the url mappings for Granite AMF remoting and for Gravity push graniteUrlMapping and gravityUrlMapping.
Tide additionally provides a built-in DefaultSecureServiceInitializer with the same options to setup a https channel.
Finally you can completely customize the channel initialization by providing your own implementation of IServiceInitializer. It has only one method initialize that is called for all {{RemoteObject}}s or {{Consumer}}s of the applications.
Message interceptor
If you need some common behaviour for all remote calls, such as showing/hiding a wait screen at each call or setting custom headers, you can implement a message interceptor that will be called before and after each call.
public class MyMessageInterceptor implements IMessageInterceptor {
public function before(msg:IMessage):void {
showWaitScreen();
msg.headers['customHeader'] = 'test';
}
public function after(msg:IMessage):void {
var customHeader:String = msg.headers['customHeader'] as String;
hideWaitScreen();
}
}
Additional features
There are a few other useful features :
- The static property Tide.showBusyCursor can enable or disable the busy mouse cursor during execution of remote calls.
- Tide.busy is a bindable property that can be used to determine if there is currently a remote call in progress.
- Tide.disconnected is a bindable property that can be used to determine is the network connection is currently broken. If becomes false when a network error is detected and set to true at each successful call.
