Learning Flex – Lesson 18, Accessing Server Side Objects

where <mx:WebService> uses XML based transfer, <mx:RemoteObject> uses the binary Action Message Format (AMF) to communicate between server and presentation layer. This means it’s faster (see James Ward’s post on this). Another advantage is the capability to pass native objects directly which saves you from translating basic generic Objects to your richer custom classes.

Flex supports communication with ColdFusion, Java (via Livecycle Data Services (LCDS), the open source BlazeDS or other 3rd party apps such as GraniteDS) or PHP (Zend Framework) and .Net (Fluorine FX) . The AMF spec is available here so other implementations may also be available.

This post will deal with configuration basics but you can look at some of my prior posts for some references and help with working with BlazeDS. The other framework sites linked to above will help you with the alternatives.

You need to use a configuration file (generally services-config.xml) to define how to make calls to the server. To use this in a standalone app (ie not deployed on a server) you need to specify it’s location in the additional compiler arguments using the -services switch.

This configuration file will define three main sections as follows:

Technology Adapter

This defines the class that translates the AMF message for the technology you’re using (Java PHP etc). The <adapters> tag contains an <adapter-definition> tag which has an id, class, and optional default (boolean) attributes.

Channels

This section defines the location of the remote server and the type of channel to be used (AMFChannel, SecureAMF, etc). A <channel-definition> tag has an id and class attribute and contains an <endpoint> tag which contains a url and class. The endpoint url may make use of special tokens {server.name}, {server.port} and {context.root} which are replaced with the server name, port and web app context root based on the url the swf is served from.

Depending on the type of channel, it may have additional parameters declared within a <properties> tag. A <default-channels> tag may be used to specify a <channel> tag with a ref attribute which is the id of the default channel to use.

Destination Handle

This is what the Flex code will refer to when using a remote object. A <destination> tag has an id attribute and contains a <properties> tag which will have a <source> tag to define either * (meaning all) or a specific class the destination is allowed to deal with on the server. A destination may also specify it’s own channels if it doesn’t use a default.

Defining a Remote Object

A <mx:RemoteObject> tag has an id attribute to reference it by and a destination attribute which is the id of the destination to use from the configuration. If you did not specify a specific class in the destination config, the source attribute should be used to specify the server class to use. You may also define result and fault event handlers. showBusyCursor is a boolean attribute that can be used to graphically indicate you’re talking to the server. It’s purely graphical and does not stop the user from interacting with the application.

As an alternative to the result event handler, you can bind directly to the lastResult of a remote object method call similarly to the way you can for a HttpService eg {ro.getResultList.lastResult} where ro is the id of my remote object and getResultList is a method on the server object. You call a remote object method in the same way you do a web service call.

Mapping ActionScript Objects to Server Objects

It’s possible  to define a mapping between an ActionScript class and it’s equivalent on the server such that when an object arrives from the server, it’s automatically converted into the ActionScript version rather than a generic Object.

To achieve this, a RemoteClass metadata tag is placed before the class definition with an alias to the server side version for example:

[RemoteClass (alias="com.mycompany.MyClass")]
public class MyClass{...

To prevent a property from being sent to the server use the [Transient] metadata tag above the declaration of that property.

As long as the number of properties and their names match, the mapping will occur. If either version of the class changes, Flex will revert to returning a generic object.

Flex Security

If you’re using BlazeDS/Livecycle or some other backend integration services, you might want to take a look at deblaze. It’s a security tool that can be used to figure out exposed remote methods and other info. It gives you some examples about how you could use it and pointers to make things more secure.

Spring Application Picture

Well I managed to get the database to talk to me again so here’s the picture of the app I promised -

Seam app

One more thing…

One thing I omitted from my previous post is I developed this app when I still had my Flex Builder trial. This is important because when you’re using remote objects in Flex Builder, you specify the server you’re going to connect to as part of your project setup and this gets compiled into the swf that’s created.

Obviously that’s not going to help you if you’re not using Flex Builder (and having your server pre-defined at compile time is a bit of an issue if you’re a “real” developer working with dev, test & prod environments).

I was thinking about digging through the documentation to figure something out but Christophe Coenraets came to the rescue with a post on externalizing your service configuration that has it covered.

Flex Front End For Seam & JBoss

This covers the front end of the server side app I talked about in my previous post.

The demo Oracle database I’d linked my Seam app to had a little table with addresses so I decided to display them in a datagrid and use a Google Map display to go to any address selected in the datagrid.

I swiped the Google Map code from some experiments I’d done with that API so it also has a function that will display the lat,long for a position clicked in the map. It doesn’t really serve any purpose here so consider it a little extra demo.

Here’s the code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:ns1="com.google.maps.controls.*" applicationComplete="ro.getResultList()">
<mx:RemoteObject id="ro" destination="seam-userDB"/>
<mx:Script>
<![CDATA[

import com.google.maps.LatLng;
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.MapType;
import com.google.maps.MapMouseEvent;
import com.google.maps.InfoWindowOptions;
import com.google.maps.controls.MapTypeControl;
import com.google.maps.controls.ZoomControl;
import com.google.maps.services.ClientGeocoder;
import com.google.maps.services.GeocodingEvent;
import com.google.maps.overlays.Marker;
import com.google.maps.overlays.MarkerOptions;

import mx.controls.Alert;

private function onMapReady(event:Event):void {
this.map.setCenter(new LatLng(40.736072,-73.992062), 14, MapType.NORMAL_MAP_TYPE);
this.map.addEventListener(MapMouseEvent.CLICK, onMapClick);
this.map.addControl(new ZoomControl());
this.map.addControl(new MapTypeControl());
}

private function onMapClick(event:MapMouseEvent):void {
map.openInfoWindow(event.latLng, new InfoWindowOptions({title: "Location", content: event.latLng.toString()}));
}

private function mapGoto():void {
// Geocoding example
var geocoder:ClientGeocoder = new ClientGeocoder();

geocoder.addEventListener(
GeocodingEvent.GEOCODING_SUCCESS,
function(event:GeocodingEvent):void {
var placemarks:Array = event.response.placemarks;
if (placemarks.length > 0) {
map.setCenter(placemarks[0].point);
var marker:Marker = new Marker(placemarks[0].point);

marker.addEventListener(MapMouseEvent.CLICK, function (event:MapMouseEvent):void {
marker.openInfoWindow(new InfoWindowOptions({content: placemarks[0].address}));
});
map.addOverlay(marker);
}
});
geocoder.addEventListener(
GeocodingEvent.GEOCODING_FAILURE,
function(event:GeocodingEvent):void {
Alert.show("Sorry Couldn't find that place!");
trace(event);
trace(event.status);
});
geocoder.geocode(address.text);
}
]]>
</mx:Script>

<mx:Style>
Alert
{
fontFamily:"Times New Roman";
fontSize:14;
}
</mx:Style>

<mx:Panel title="Matt's Maps" width="100%" height="100%" fontSize="14" fontFamily="Times New Roman" fontWeight="bold">
<maps:Map xmlns:maps="com.google.maps.*" id="map" mapevent_mapready="onMapReady(event)" width="100%" height="100%" key="ADD YOUR GOOGLE MAP KEY HERE"/>
<mx:ControlBar>
<mx:DataGrid id="dg" dataProvider="{ro.getResultList.lastResult}" width="100%" height="100%" itemClick="mapGoto()"/>

<mx:Button label="Get Data" click="ro.getResultList()"/>

</mx:ControlBar>
<mx:Label id="address" text="{dg.selectedItem.custStreetAddress1} {dg.selectedItem.custCity} {dg.selectedItem.custState}"/>
</mx:Panel>

</mx:Application>

The Application node includes the google namespace and instructs the app to call the remote object’s getResultList method.

The next line defines the remote object and links it to the destination we declared on the server.

The onMapReady method is called when the map ready event is fired and sets the map to center in Manhattan NY, adds zoom and map type controls.

The onMapClick method is the little piece I added to show the lat,long of a point clicked in the map.

mapGoto is where all the real work is done. This adds two geocode event listeners. One for success (in which case the map is moved to the location and a marker added, together with a click event listener to pop up the address in a window if it’s clicked). One for failure (in which case an alert is shown).

After those event listeners are registered, the method calls the geocoder with an address. The geocoder is responsible for translating an address into a map location.

That concludes the embedded ActionScript code, the remainder is more presentation.

There’s a small style definition to use Times New Roman as the font at 14 point size and then the screen layout.

All of that should be pretty self-evident with the dataProvider for the data grid specified as the last result of the remote object method call and the address label being constructed from the address elements of the selected line in the data grid.

I was planning to include a screenshot of the running app but unfortunately the demo database I was hooked up to wasn’t under my control and it’s currently offline. If I can get it going, I’ll add a screenshot – until then, you’ll have to take my word for it!

JBoss, Seam and BlazeDS

Back when I still had a working Flex Builder, I wanted to do some further prototyping using BlazeDS so I decided to try and get it working in a JBoss (V 4.2.4)install I had going with a Seam (V 2.0.2) app. If you install the Eclipse plugin for Seam, you get the option to generate a “Seam Entity” for a given database table which builds out a simple CRUD app for you. I wanted to see if I could install BlazeDS and use it to hook the Seam generated ejb up to a Flex front end.

Here’s what I had to do:

Create a flex directory under the WEB_INF directory of the web project Seam created. Copy over the four config.xml files you find under the tomcat\webapps\blazeds\WEB-INF\flex directory of your BlazeDS install. (There are template versions under resources\config).

In the services-config.xml, swich the login-command from server=”Tomcat” to server=”JBoss”.

In the remoting-config.xml. comment out or remove all the tutorial destinations and add one for your Seam bean. mine looks like this:

<destination id="seam-userDB">
  <properties>
    <source>org.domain.seamflex.session.DemoCustomersList</source>
  </properties>
</destination>

(my project name is seamFlex)

In your Seam-ejb project, add the library jars found in resources\lib of the BlazeDS install (except for the commons* ones – you should already have those). This is a bit of a kitchen sink approach and you might be able to trim this down if you care to.

You also need to add some configuration to your web.xml :

<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
<!-- MessageBroker Servlet -->
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<init-param>
<param-name>flex.write.path</param-name>
<param-value>/WEB-INF/flex</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>

when I tried to run with just this, I ran into initialization issues and after hunting around, I came up with the following fix:

<!-- added this filter for flex stuff -->
<filter>
<filter-name>Seam Context Filter</filter-name>
<filter-class>org.jboss.seam.web.ContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Seam Context Filter</filter-name>
<servlet-name>MessageBrokerServlet</servlet-name>
</filter-mapping>
<!-- end of flex stuff -->

Finally, copy your Flex app folder into the WebContent directory and things should all work fine. I’ll write more about the Flex app I developed for this in my next post.

Posted in BlazeDS, Flex. Tags: , , . 9 Comments »

Flex, Java & BlazeDS

Coming from a Java background, BlazeDS really interests me. Basically it’s Adobe’s open source version of it’s LiveCycle Data Services (LCDS). Basically they’re a set of JAR files deployed on your server to improve the comunication between a Java backend and a Flex presentation layer by comunicating via AMF (Action Message Format). You can always comunicate out of Flex using http services and consuming XML but that’s not the fastest way to work.

James Ward’s site has some benchmarks for comunicating via AMF with BlazeDS.

Chirstophe Coenraets has lots of great stuff on his blog including plenty of BlazeDS stuff. This post he wrote for Adobe Devnet has everything to get you started using BlazeDS including a download of a pre-configured Tomcat server and examples to work through.

One thing to note – if you can’t get the database update exercise to work, that’s because the example has the update statement created but never executes it.

Adobe doesn’t go out of it’s way to outline the differences between BlazeDS and LCDS but Sujit has a nice clear post here that does a good job explaining it.

With the different channel choices available, I think this post from Damon Cooper’s blog does a good job of exploring the pros and cons of each.

Follow

Get every new post delivered to your Inbox.