The url should match the pattern in the dynamic url in the destination that’s defined on the server: Using the Remoting Service The Remoting Service enables you to execute public methods
Trang 1Desktop Flex applications deployed with Adobe AIR don’t need to use the Proxy Service These applications aren’t downloaded from the Web at runtime, so they aren’t subject to the rules of the Web security sandbox and can make runtime requests directly to any domain n
The application in Listing 26.2 uses a proxied request for data in an XML file stored on the server
myData = event.result.contacts.row;
} private function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.faultString, event.fault.faultCode);
} ]]>
</fx:Script>
<s:Button label=”Get Data” click=”contactService.send()”/>
<mx:DataGrid dataProvider=”{myData}”/>
</s:Application>
Trang 2On the Web
The code in Listing 26.2 is available in the Web site files as DefaultProxyDestination.mxml in the src
folder of the chapter26 project n
When you run the application in its current state, it correctly downloads and displays the requested data If you remove the HTTPService component’s useProxy property or set it to
false, the request fails because the domain of the XML file and the domain from which the cation is downloaded don’t match The result is a security fault, as shown in Figure 26.5
Using named destinations
A named Proxy Service destination uses an id other than DefaultHTTP You can use a named destination in two ways:
l When the named destination contains a nested <url> element, it represents an alias for a single Web resource The destination id can then be referred to in the Flex application instead of the actual url
l When the named destination contains one or more nested <dynamic-url> elements, it can proxy multiple Web resources
Trang 3To create a named destination for HTTPService that serves as an alias for a single Web resource, add a single <url> element nested within the destination’s <properties> element Set the
<url> element’s text value to the explicit address of the Web resource The following declaration creates a destination with an id of contactsXML that points to the location of the data in the remote domain:
<destination id=”contactsXML”>
<properties>
<url>
http://127.0.0.1:8400/blazeds/flex4bible/data/contacts.xml </url>
prop-Try these steps to use a destination that’s already been created in proxy-config.xml:
1 Open DefaultProxyDestination.mxml
2 Choose File ➪ Save As from the Flash Builder menu, and name the new file
NamedProxyDestination.mxml.
3 Locate the <mx:HTTPService> tag.
4 Remove the url and useProxy properties.
5 Add a destination property set to contactsXML The HTTPService declaration
should look like this:
<mx:HTTPService id=”contactService”
destination=”contactsXML”
result=”resultHandler(event)”
fault=”faultHandler(event)”/>
6 Run the new version of the application, and test retrieving data from the server
The proxied request should be completed successfully
On the Web
The completed code for this exercise is available in the Web site files as
NamedProxyDestinationComplete.mxml in the src folder of the chapter26 project n
Trang 4You also can include <dynamic-url> elements in a named destination, either along with or instead of the <url> element This declaration uses the same destination and a dynamic url:
To use a dynamic url in a named destination, set the HTTPService or WebService object’s
destination and url properties The url should match the pattern in the dynamic url in the
destination that’s defined on the server:
Using the Remoting Service
The Remoting Service enables you to execute public methods of server-side Java classes hosted by LCDS or BlazeDS The Flex application uses the RemoteObject component to execute the calls and handle results returned from the remote server
Trang 5The RemoteObject component is one of the Flex framework’s three Remote Procedure Call (RPC) components The other two, the WebService and HTTPService components, have been described previously Like these two, the RemoteObject component makes calls asynchronously and handles returned results with either binding expressions or event handlers.
The Remoting Service on the server and the RemoteObject component on the client use a binary format to transfer data back and forth This format, AMF (Action Message Format), was originally created for ColdFusion’s Flash Remoting technology and was then adapted for use with Java classes
in Flex Data Services, LiveCycle Data Services, and now BlazeDS Because this format is binary, the result is smaller data bundles, and there is no need for resource-intensive XML parsing In most cases, the result is better speed and performance
Note
There are two versions of AMF The first version, now known as AMF0, was originally supported in earlier sions of ColdFusion and Flex 1.x The newer version, known as AMF3, is supported by the current versions of ColdFusion, LiveCycle Data Services, and BlazeDS Flex 3 and Flex 4 applications make requests in AMF3 by default, but they can be configured to communicate in AMF0 when required n
ver-Creating and exposing Java classes
The RemoteObject component can call public methods of any basic Java class that’s been hosted and configured in LCDS or BlazeDS (For convenience, I’ll refer exclusively to BlazeDS for the rest of this chapter, but the functionality and techniques are exactly the same for LiveCycle Data Services.)
In the past, a number of individuals and organizations reverse-engineered AMF to create open-source
or commercial server implementations that are compatible with Flex applications OpenAMF (http://
sourceforge.net/projects/openamf/), Red5 (http://osflash.org/red5), AMFPHP (www
amfphp.org), and WebORB (www.themidnightcoders.com/weborb/java/) all represent tial alternatives to Adobe’s own products for providing AMF-based messaging with Java-based applica-tion servers
poten-In February 2008, Adobe Systems publicly documented both AMF0 and AMF3 so that organizations that had previously implemented AMF-capable servers could verify that their work matched the proto-col exactly and to allow new participants in the world of Flex development to get it right the first time
The AMF documentation is currently available from these links:
AMF0:
http://opensource.adobe.com/wiki/download/attachments/1114283/amf0_
spec_121207.pdf?version=1AMF3:
http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_
spec_121207.pdf?version=1
AMF Documentation
Trang 6You need to follow two steps when making the Java methods available:
1 Create and compile a Java class, and place in the BlazeDS classpath.
2 Create a destination that points to the Java class on the server.
Any plain old Java Object (sometimes known as a POJO) can be used through the Remoting Service Classes written in other common Java design patterns, such as servlets and Enterprise Java Beans (EJBs), can’t be called directly through the Remoting Service If you have existing functional-ity already built in these formats, though, it’s a fairly easy task to create a POJO to call from Flex that in turn makes calls on the server to existing functions
Follow these rules for creating Java classes for use with the Remoting Service:
l All classes must be in the BlazeDS classpath
l For individual classes, you can accomplish this by placing them in BlazeDS’s INF/classes folder As with all Java Enterprise Edition applications, classes placed
WEB-in this folder are automatically available to the application
l For classes stored in JAR (Java Archive) files, the JAR file can be placed in BlazeDS’s
WEB-INF/lib folder As with all Java Enterprise Edition applications, archive files placed in this folder are automatically added to the classpath when the application qis started
l The Java class must have a no-arguments constructor method or no explicit structor methods at all At runtime, the Remoting Service gateway creates an instance of
con-the Java class (static methods aren’t supported) It assumes con-the presence of a constructor method that can be called with no arguments:
public ROService(){
System.out.println(“constructor method called”);
}
If you create a class with no explicit constructor method, the Java compiler adds the no-arguments constructor for you If there’s at least one constructor method with arguments, though, you’re responsible for creating the alternative constructor method with no arguments
l All methods must be explicitly marked as public Java enables you to drop the access
modifier from a method declaration, but these methods aren’t available to the Remoting Service This simple Java-based method is suitable for use by the Remoting Service:
public String helloWorld(){
return “Hello from the world of Java!”;
}
l You can’t use a small set of reserved method names These methods are used by the
gateway library at runtime; if your class implements any of these method names, conflicts can result:
l addHeader()
l addProperty()
Trang 7l Method names should not start with an underscore (_) character
Listing 26.3 shows the source code for a Java class named ROService in the flex4bible
package It has an explicit no-arguments constructor method and a single method that returns a
public String helloWorld() { return “Hello from the world of Java”;
} public List getArray() { Map stateObj;
List ar = new ArrayList();
stateObj = new HashMap();
Trang 8“ and “ + val2;
} public String setContact(Contact myContact) { return “Contact sent from server: “ + myContact.getFirstName() + “ “ + myContact.getLastName();
} public Contact getContact(String val1, String val2) { Contact myContact = new Contact();
myContact.setFirstName(val1);
myContact.setLastName(val2);
return myContact;
} }
On the Web
The source code in Listing 26.3 is available in the Web site files as ROService.java in the BlazeDS
WEB-INF/src folder The compiled version of the class is stored in BlazeDS WEB-INF/classes folder n
Note
The no-arguments constructor method in Listing 26.3 isn’t required as long as the class doesn’t have any other constructor methods n
Configuring Remoting Service destinations
Each Java class you want to call from a Flex application with the Remoting Service must be ured as a destination in the BlazeDS configuration files Remoting Service destinations are defined
config-in remoting-config.xml in the BlazeDS WEB-INF/flex folder
The default remoting-config.xml that’s delivered with a fresh BlazeDS installation looks like this:
Trang 9Each Java class you want to call from Flex should be configured as a destination Each destination
is declared as a child of the configuration file’s <service> root element and looks like this in its simplest form:
applica-l The <source> element This element is required and is set to the fully qualified name
and package of the Java class that contains methods you want to call
l The <scope> element This element is optional and is set to one these three values:
l application means that a single instance of the Java class is constructed as BlazeDS
starts up and is shared by all users and requests
l session means that a new instance of the Java class is constructed for each new
browser session As each user sends new requests, the session instances are tracked (via the host application server’s session management) with cookies that are automati-cally generated and tracked by BlazeDS and the hosting application server
l request (the default) means that a new instance of the Java class is constructed for
each call to any of the class’s methods
Trang 10All other things being equal, you achieve the best performance and most efficient memory usage on the server with <scope> set to application The only reason to use the default setting of request is if the Java class has code that can’t be called safely by concurrent requests from multiple clients n
Using the RemoteObject Component
You use the Flex SDK’s RemoteObject component to make calls to a server-side Java class’s lic methods Just as the HTTPService component sends and receives requests with generic XML-formatted messages and the WebService component does with SOAP-based Web services, the
pub-RemoteObject component makes requests and handles responses using the HTTP tion protocol
communica-The big difference with RemoteObject is the message format: Because AMF is binary, instead of the text-based XML languages used by the WebService and HTTPService components, mes-sages formatted in AMF are a fraction of the size generated by the other RPC components As a result, communication is faster, less network bandwidth is used, and larger data packets can be transferred between client and server
Instantiating the RemoteObject component
As with the HTTPService and WebService components, you can instantiate RemoteObject
in MXML or ActionScript code When used with BlazeDS, you instantiate the object and set its tination property
des-This MXML code creates an instance of the RemoteObject component that points to a side destination:
server-<s:RemoteObject id=”roHello” destination=”helloService”/>
The equivalent code in ActionScript looks like this:
import mx.rpc.remoting.RemoteObject;
private var roHello:RemoteObject = new RemoteObject(“roHello”);
Alternatively, you can first declare the object and then set its destination:
var roHello:RemoteObject = new RemoteObject();
roHello.destination = “roHello”;
Calling remote methods
You call public methods of server-side Java classes as though they were local methods of the
RemoteObject For example, the Java class in Listing 26.3 has a public method named
helloWorld() that returns a simple String As with local functions, you can call the remote
Trang 11method upon any application event For example, this code calls the server-side helloWorld()
method upon a Button component’s click event:
<s:Button label=”Click to say hello”
click=”roHello.helloWorld()”/>
You also can call a remote method by calling the RemoteObject object’s getOperation()
method to create an instance of the Operation class The following code creates the Operation
object and then calls its send() method to call the remote method:
This technique enables you to determine which remote method will be called at runtime, instead
of having to hard code the method name
Handling RemoteObject results
As with the other RPC components, you can handle data returned from a call to a remote method with binding expressions or event handlers Binding expressions take less code and are easy to cre-ate, while an event handler gives you much more flexibility in how you receive, process, and save data to application memory
Using binding expressions
A binding expression used to pass returned data to application components consists of three parts, separated with dots:
l The RemoteObject instance’s id
l The remote method name
l The lastResult property
At runtime, the method is created as an Operation object that’s a member of the
RemoteObject instance with an id that matches the method’s name The Operation object’s
lastResult property is populated with data when it’s received from the server
The lastResult property is typed as an ActionScript Object, but at runtime its native type is determined by what type of data was returned from the server A String returned from Java is translated into an ActionScript String value, so a binding expression that handles the value returned from the simple helloWorld() method can be used to pass the returned value to a
Label or other text display control
The application in Listing 26.4 calls the remote helloWorld() method and displays its returned data in a Label control with a binding expression in its text property
Trang 12LISTING 26.4 Handling returned data with a binding expression
<s:Label text=”Hello from BlazeDS!” fontSize=”14” fontWeight=”bold”/>
<s:Button label=”Click to say hello” click=”roHello.helloWorld()”/>
Using the result event
As with the other RPC components, you can handle results of a call to a remote method with the
RemoteObject component’s result event in an identical fashion This event dispatches an event object typed as mx.rpc.events.ResultEvent, the same event object that’s used by the other RPC components: HTTPService and RemoteObject The event object’s result prop-erty references the returned data
To handle and save data using the result event, follow these steps:
1 Declare a bindable variable outside of any functions that acts as a persistent
refer-ence to the returned data.
2 Cast the variable’s type depending on what you expect to be returned by the remote
method For example, if the data returned by the remote Java-based method is typed as a
primitive array or an implementation of the Java List interface, the RemoteObject
component casts the returned data as an ArrayCollection:
import mx.collections.ArrayCollection;
[Bindable]
private var myData:ArrayCollection
3 Create an event handler function that will be called when the event is dispatched
The function should receive a single event argument typed as ResultEvent and return void:
Trang 13private function resultHandler(event:ResultEvent):void{
}
4 Within the event handler function, use the event.result expression to refer to the data
that’s returned from the server Just as with the WebService component, ResultEvent.result is typed as an Object Because the expression’s native type differs depending on what’s returned by the remote method, you typically have to explicitly cast the returned data This code expects the remote method to return an ArrayCollection:
myData = event.result as ArrayCollection;
You can listen for the result event with either an MXML attribute-based event listener or a call to the ActionScript addEventListener() method The attribute-based event listener looks like this:
<s:RemoteObject id=”roHello” destination=”helloService”
private var statesData:ArrayCollection;
private function resultHandler(event:ResultEvent):void {
statesData = event.result as ArrayCollection;
}
continued
Trang 14As with the other RPC components, exceptions that occur during execution of remote methods generate a
fault event The code to handle faults is exactly the same as with the other RPC components For a full description and some code examples, see Chapter 21 and Chapter 23 n
Note
As with the HTTPService and WebService components, you can pass result and fault event objects to ActionScript event handler functions using the ItemResponder and AsyncToken classes See Chapter 21 for details n
Working with multiple methods
When you need to call more than one method of a Java class on the server, you have to distinguish which event handler function should be called for each of them You do this in MXML with the
<mx:method> compiler tag, which is nested within a <mx:RemoteObject> tag set Each
<mx:method> tag represents a remote Java method and can declare its own distinct result and event handlers
The Java class in Listing 26.6 has a number of different methods Its helloWorld() method returns a String, getArray() returns a List, and so on
LISTING 26.6 The Java class with methods being called from Flex
Trang 15public class ROService { public String helloWorld() {
return “Hello from the world of Java”;
} public List getArray() {
Map stateObj;
List ar = new ArrayList();
stateObj = new HashMap();
return “You passed values “ + val1 + “ and “ + val2;
} public String handleObject(Contact myContact) {
return “You Contact # “ + myContact.getContactId() + “: “ + myContact.getFirstName() + “ “ + myContact.getLastName();
} }
On the Web
The code in Listing 26.6 is available in the Web site files as ROService.java in the BlazeDS application’s
WEB-INF/src/flex4Bible folder The compiled version of the class is stored in WEB-INF/classes n
A Flex application that needs to call more than one of these methods would use the <s:method>
tag as in the following example:
<s:RemoteObject id=”roHello” destination=”helloService”
result=”arrayHandler(event)”>
<s:method name=”helloWorld” result=”helloHandler(event)”/>
<s:method name=”getArray” result=”arrayHandler(event)”/>
</s:RemoteObject>
Trang 16Each method’s custom event handler function would then expect the appropriate type of data to be returned from its remote method.
The application in Listing 26.7 handles the result events of multiple remote methods using an MXML declaration
LISTING 26.7 Handling multiple remote methods’ result events
<s:method name=”helloWorld” result=”helloHandler(event)”/>
<s:method name=”getArray” result=”arrayHandler(event)”/>
private var statesData:ArrayCollection;
[Bindable]
private var helloString:String;
private function arrayHandler(event:ResultEvent):void {
statesData = event.result as ArrayCollection;
} private function helloHandler(event:ResultEvent):void {
helloString = event.result as String;
} ]]>
</fx:Script>
<s:layout>
<s:VerticalLayout horizontalAlign=”center” paddingTop=”20”/>
</s:layout>
<s:Button label=”Get String” click=”roHello.helloWorld()”/>
<s:Label text=”{helloString}” fontSize=”14”/>
<s:Button label=”Get Array” click=”roHello.getArray()”/>
<mx:DataGrid dataProvider=”{statesData}”/>
</s:Application>
Trang 17On the Web
The code in Listing 26.7 is available as ROMultipleMethods.mxml in the src folder of the chapter26
zip file n
Using the CallResponder class
The CallResponder class enables you to define unique responses to method calls In contrast to using the <s:method> tag, which defines responses in a way that makes it difficult to modify at runtime, the CallResponder enables you to hook up method calls to particular responses using ActionScript code You can wrap the calls with whatever logic you deem necessary to implement your application’s required logic
If you’ll be calling multiple methods from a single Java class that’s been exposed as a Remoting Service destination, you can declare one instance of the RemoteObject class and one or more instances of CallResponder Each CallResponder can define its own result and fault
event handlers:
<fx:Declarations>
<s:RemoteObject id=”roHello” destination=”helloService”/>
<s:CallResponder id=”helloResponder” result=”helloHandler(event)”/>
<s:CallResponder id=”arrayResponder” result=”arrayHandler(event)”/>
</fx:Declarations>
When you call a remote method, you receive an instance of the AsyncToken class By assigning a
CallResponder object’s token property to that AsyncToken object, you connect the responder
to the method call:
<s:RemoteObject id=”roHello” destination=”helloService”/>
<s:CallResponder id=”helloResponder” result=”helloHandler(event)”/>
<s:CallResponder id=”arrayResponder” result=”arrayHandler(event)”/>
continued
Trang 18private var helloString:String;
private function arrayHandler(event:ResultEvent):void {
statesData = event.result as ArrayCollection;
} private function helloHandler(event:ResultEvent):void {
helloString = event.result as String;
} ]]>
<s:Label text=”{helloString}” fontSize=”14”/>
<s:Button label=”Get Array”
Passing arguments to remote methods
As with WebService operation parameters, you can pass arguments to remote methods using either explicit or bound argument notation Explicit notation means that arguments are passed in the same order in which they’re declared in the Java method
This Java method, for example, requires two String arguments and returns a concatenated String:
public String concatValues(String val1, String val2){
return “You passed values “ + val1 + “ and “ + val2;
}
Trang 19The following ActionScript code passes arguments to this remote method with explicit syntax:
roHello.concatValues(fnameInput.text, lnameInput.text);
You also can use bound argument notation with XML elements for each argument wrapped in an
<mx:arguments> tag set This code binds the concatValues() method’s two arguments to values gathered from TextInput controls:
<mx:RemoteObject id=”roHello” destination=”helloService”>
The application in Listing 26.9 passes explicit arguments to a remote method on the server and displays the returned result with a binding expression
LISTING 26.9 Passing arguments using explicit notation
Trang 20The code in Listing 26.9 is available as ROExplicitArgs.mxml in the src folder of the chapter26.zip
file Another file named ROBoundArgs.mxml, not shown here, demonstrates the use of bound arguments n
Passing data between ActionScript and Java
Data passed from a Flex application to a Java class with the Remoting Service is serialized, or formed, from ActionScript data types to their equivalent types in Java When data is returned from Java to Flex, a similar serialization occur”s
trans-Table 26.1 describes how data is serialized from ActionScript to Java and back again
TABLE 26.1
ActionScript to Java Data Serialization
Array (dense, meaning there are no “holes” in the indexing)
List ArrayCollection
Array (sparse, meaning there is at least one gap in the indexing, or associative with non-numeric keys)
ArrayCollection List ArrayCollectionBoolean java.lang.Boolean Boolean
Int/uint java.lang.Integer Int
Trang 21ActionScript To Java Back to ActionScript
Notice that data moved in both directions doesn’t always survive the round trip with the same data type as it had at the beginning For example, a “sparse” ActionScript Array is serialized as a Java implementation of the Map interface When the same data is returned from Java to ActionScript, it arrives as an ActionScript Object instead of an Array
There are additional data conversions when returning data from Java to a Flex application
For example, both the Java Calendar and Date objects become instances of an ActionScript
Date All non-integer Java data types, such as Double, Long, and Float, are mapped
to an ActionScript Number And numeric Java types that don’t fit the precision limitations of the ActionScript Number type, such as BigInteger and BigDecimal, are mapped to an ActionScript String
Using value object classes
When passing data between a Flex client application and a Java-based server, data objects are cally built using the Value Object design pattern This pattern ensures that data is serialized in a precise manner and avoids the uncertainties of automatic object serialization described in the pre-vious section
typi-Note
The Value Object design pattern is known in various Flex and Java documentation sources as the Transfer Object and Data Transfer Object pattern The different names are all used to refer to the same pattern: a class that contains data for a single instance of a data entity n
The Java version of the value object is written with classic bean-style syntax Each value is declared
as a private field of the class and has its values set at runtime with public set and get accessor methods
The Java class in Listing 26.10 has three private fields with matching accessor methods and is able for use in a Flex application
Trang 22private String firstName;
private String lastName;
public int getContactId() { return contactId;
} public void setContactId(int contactId) { this.contactId = contactId;
} public String getFirstName() { return firstName;
} public void setFirstName(String firstName) { this.firstName = firstName;
} public String getLastName() { return lastName;
} public void setLastName(String lastName) { this.lastName = lastName;
} }
On the Web
The code in Listing 26.10 is available in the Web site files as Contact.java in the BlazeDS application’s
WEB-INF/src/flex4bible folder The compiled version of the class is stored in WEB-INF/classes n
To pass this object to a Java-based remote method, create a matching ActionScript class The ActionScript version’s properties must match the Java class in both name and data type
Trang 23values to the server-side version Similarly, if a Java-based remote method returns instances of the server-side version, client-side versions are created automatically and their property values set to the values received from the server.
The ActionScript class in Listing 26.11 declares the same set of values as public properties and maps itself to the server’s version with the [RemoteClass] metadata tag
LISTING 26.11
An ActionScript value object class for use with the Remoting Service
package vo {
[Bindable]
[RemoteClass(alias=”flex4bible.Contact”)]
public class ContactVO {
public var contactId:int;
public var firstName:String;
public var lastName:String;
public function ContactVO() {
} } }
Cross-Reference
If you’re a Java developer who has a congenital distrust of public properties, you can define your ActionScript value object classes with implicit getter and setter accessor methods and private properties In Chapter 2, I describe how Flash Builder 4 can generate the required getter and setter code for you based on existing public property declarations n
The Flex application in Listing 26.12 sends and receives instances of value object classes When it sends an ActionScript value object to the server, the Java method extracts the received object’s properties and returns a concatenated value When the Flex application sends two String values, the server’s method builds a strongly typed value object and returns it to Flex
Trang 24LISTING 26.12 Sending and receiving strongly typed value object classes
<s:RemoteObject id=”roHello” destination=”helloService”/>
<s:CallResponder id=”getResponder” result=”getHandler(event)”
[Bindable]
private var myContact:Contact;
private function setContact():void {
myContact = new Contact();
myContact.firstName = fnameInput.text;
myContact.lastName = lnameInput.text;
setResponder.token = roHello.setContact(myContact);
} private function getContact():void {
getResponder.token = roHello.getContact(
fnameInput.text, lnameInput.text);
} private function setHandler(event:ResultEvent):void {
Alert.show(event.result as String, “Received String”);
} private function getHandler(event:ResultEvent):void {
myContact = event.result as Contact;
Alert.show(“Contact VO received from server: “ + myContact.firstName + “ “ + myContact.lastName, “Received Contact value object”);
}
Trang 25private function faultHandler(event:FaultEvent):void {
<s:Button label=”Send Object” click=”setContact()”/>
<s:Button label=”Receive Object” click=”getContact()”/>