Lip Service – Using Mosaic 9.5 Services

LiveCycle Mosaic 9.5 has the concept of a service component.  This is a data store that defines objects that are used by multiple tiles. This allows the exchange of custom data objects between tiles as well as pre-configuring data before a tile is loaded.

In this post, I walk through a simple service example to explain how to put one together.  The source code for this project is located here

Why Services Matter

Why would you go through the effort of putting together a service? What does it give you that you can’t do with “normal” tiles? One good reason is that you need to exchange custom objects between tiles.

If you have multiple tiles that use the same custom object. If you were to compile each of those tiles with the object inside it and then deploy it, you would run into a problem due to the way Mosaic isolates tiles.

For example:

Say you have a complex object called Dog. A dog has several fields: name, color, breed and a Boolean called hungry. Now say you have two tiles that make use of the dog object – AddDog and ShowDogs. The AddDog tile creates a message that sends out a Dog object. The ShowDogs tile watches for a message with the Dog message in it.

When you package your tiles, each one will contain a copy of the Dog object.

clip_image002[4]

Since the Mosaic framework doesn’t know what a Dog object is and there are really two different Dog objects (one for each tile) you are going to get errors.

You could send each individual as a message (or an array of messages), but that gets cumbersome quickly. Also, if your custom object contains complex objects it becomes a problem.

Now let’s look at doing the same thing using Mosaic Services. This time you create an interface and service for the Dog object. The two tiles don’t include the Dog themselves, but they do refer to it.

clip_image002[6]

Now the framework is aware of what a Dog object is. When the AddDog tile sends a message with a Dog in it, the ShowDogs tile will be able to receive the message.

Using Mosaic Services

To create and use Mosaic services you need to do the following:

  • Create an ActionScript interface library. This allows you to separate the definition of the custom object from its implementation.
  • Create the ActionScript service, an implementation of the interface class.
  • Use the services in your tiles. You will be accessing the custom objects using the interface.
  • Add the services, interfaces and tiles to the catalog
  • Add the service reference to the application
  • Deploy the catalog and application to your Mosaic server.

When the user logs in, Mosaic will use the designated service implementation for the custom object based on the references in the application and catalog information. For those of you that are familiar with the Java Spring framework this is similar to Spring’s dependency injection.

Now let’s look at how to build the above mentioned example using services.

The Interface Library

Using FlashBuilder create a new Flex Library Project to hold the interface file(s). Ensure that you are using Flex 3.4 or greater. The interface spells out the fields and functions that must be in the service implementation.

In this case I only have one interface, IDogData. It defines the name, color, breed and hungry fields as well as a toString function that is useful for debugging.

package com.adobe.etech

{

public interface IDogData

{

function get name():String;

function set name(nm:String):void;

function get color():String;

function set color(clr:String):void;

function get breed():String;

function set breed(brd:String):void;

function get hungry():Boolean;

function set hungry(hngy:Boolean):void;

function toString():String;

}

}

The interface library needs to be compiled into a SWF file so it can be deployed to Mosaic. The easiest way to do this is to modify the sample ant scripts that are shipped as part of the stockDataService that comes with Mosaic.

The Service Implementation Library

Now that we have defined what the custom object will do, we need to actually do it. Create another Flex Library Project to hold the service classes. You will need to add a reference to the interface library you just created as well as a reference to the Mosaic tile API swc.

Next create ActionScript implementation classes that implement your interface library classes. The service class will need to have corresponding functions for each of the ones mentioned in the interface. This is where you put the code that carries out the operations that you named in the interface.

In this example, I have only one service class (DogData) that implements the IDogData interface. The service class fleshes out each of the functions from the interface. I have also added a constructor function that allows me to initialize the service at start up. I’ll talk more on that when I discuss the catalog descriptor.

package com.adobe.etech.impl

{

import com.adobe.etech.IDogData;

public class DogData implements IDogData{

private var _name:String;

private var _color:String;

private var _breed:String;

private var _hungry:Boolean;

public function DogData(nm:String=“not set”, clr:String=“not set”, brd:String=“not set”, hngry:Boolean=false){

_name = nm;

_color = clr;

_breed = brd;

_hungry = hngry;

trace (“*********** Dog: “ + this.toString());

}

public function toString():String{

return (_name + “, “ + _color + “, “ + _breed + “, “ + _hungry.toString());

}

public function get hungry():Boolean{

return _hungry;

}

public function set hungry(value:Boolean):void{

_hungry = value;

}

public function get breed():String{

return _breed;

}

public function set breed(value:String):void{

_breed = value;

}

public function get color():String{

return _color;

}

public function set color(value:String):void{

_color = value;

}

public function get name():String{

return _name;

}

public function set name(value:String):void{

_name = value;

}

}

}

Much like the interface library, the service library must be compiled into a SWF. Again, the stockDataService includes an ANT script that serves as a good starting point for how to do this.

Services in Tiles

Now that you have an interface library and a service implementation of that interface; its time to look at how the service is used in your tiles.

Start by creating a new Flex project to hold your tile source code. This project will be like any other Mosaic tile project except that you will include a reference to the service implementation library that you created earlier. I won’t get into creating general Mosaic tiles here, as you can find that information in other Adobe documents.

In your tile code, you will refer to the interfaces and not the service implementations. This is because we want Mosaic to select the correct services to ensure that we are using ones common to all tiles. We don’t want a copy of the service embedded in our tile code.

For example: I have a very simple tile called AddDogs. I want it to share the dog object, specified in the IDogData interface, that is loaded into the application. This will allow me to send IDogData objects as part of Mosaic messages. My Tile code looks like this:

<?xml version=”1.0″ encoding=”utf-8″?>

<mc:Tile xmlns:fx=”http://ns.adobe.com/mxml/2009

xmlns:s=”library://ns.adobe.com/flex/spark

xmlns:mx=”library://ns.adobe.com/flex/mx

xmlns:mc=”com.adobe.mosaic.core.*

layout=”absolute” width=”100%” height=”100%

>

<fx:Script>

<![CDATA[

import com.adobe.etech.IDogData;

import mx.controls.Alert;

[Bindable] public var dogData:IDogData;

protected function addBtn_clickHandler(event:MouseEvent):void{

dogData.name = dname.text;

dogData.color = dcolor.text;

dogData.breed = dbreed.text;

dogData.hungry = hungry.selected;

this.parentView.context.setAttribute(“changedDog”,dogData);

}

]]>

</fx:Script>

<fx:Declarations>

<s:RadioButtonGroup id=”hungryGroup/>

</fx:Declarations>

<mx:Form>

<mx:FormHeading label=”Add a new dog/>

<mx:FormItem label=”Dog’s Name>

<mx:TextInput id=”dname/>

</mx:FormItem>

<mx:FormItem label=”Color>

<mx:TextInput id=”dcolor/>

</mx:FormItem>

<mx:FormItem label=”Breed>

<mx:TextInput id=”dbreed/>

</mx:FormItem>

<mx:FormItem label=”Hungry” id=”dhungry>

<s:RadioButton x=”76” y=”20” id=”hungry” label=”Yes” value=”true” groupName=”hungryGroup/>

<s:RadioButton x=”76” y=”46” id=”nothungry” label=”No” value=”false” groupName=”hungryGroup/>

</mx:FormItem>

<mx:FormItem>

<mx:Button id=”addBtn

label=”Add

click=”addBtn_clickHandler(event)”/>

</mx:FormItem>

</mx:Form>

</mc:Tile>

I have another tile that also uses the IDogData object – ShowDogs. In this case the tile is listening for a message with the dog object and will show it in fields:

<?xml version=”1.0″ encoding=”utf-8″?>

<mc:Tile xmlns:fx=”http://ns.adobe.com/mxml/2009

xmlns:s=”library://ns.adobe.com/flex/spark

xmlns:mx=”library://ns.adobe.com/flex/mx

xmlns:mc=”com.adobe.mosaic.core.*

layout=”absolute” width=”100%” height=”100%

creationComplete=”init()”>

<fx:Declarations>

<!– Place non-visual elements (e.g., services, value objects) here –>

</fx:Declarations>

<fx:Script>

<![CDATA[

import com.adobe.etech.IDogData;

import mx.events.FlexEvent;

import mx.events.PropertyChangeEvent;

import mx.controls.Alert;

[Bindable] public var dogData:IDogData;

private function init():void{

getDogData();

this.parentView.context.addAttributeWatcher(“changedDog”,onDogChange);

}

private function onDogChange(e:PropertyChangeEvent):void{

getDogData();

}

private function getDogData():void{

dogname.text = dogData.name;

dogcolor.text = dogData.color;

dogbreed.text = dogData.breed;

doghungry.text = dogData.hungry.toString();

//Alert.show(dogData.toString(),”Change in Dog”);

}

]]>

</fx:Script>

<s:Label x=”51” y=”48” text=”Name/>

<s:Label x=”51” y=”68” text=”Color/>

<s:Label x=”50” y=”88” text=”Breed/>

<s:Label x=”50” y=”108” text=”Hungry/>

<s:Label x=”115” y=”49” id=”dogname/>

<s:Label x=”115” y=”69” id=”dogcolor/>

<s:Label x=”114” y=”89” id=”dogbreed/>

<s:Label x=”114” y=”109” id=”doghungry/>

</mc:Tile>

Adding Services to the Catalog

I mentioned earlier that the two tiles use the object interface and not any particular implementation of that interface. This is because we only want one implementation loaded, and that should not be in any particular tile. When the tile code runs, however; it will need an instantiated version of the object on which to work.

The Mosaic catalog is used to resolve this issue. In the catalog we will add the service implementation. In the tile descriptions we will create a reference to that service implementation. Mosaic will resolve the references so the running code has access to the instantiated object.

On the file system, in the catalogs folder, we need to add an Interfaces directory and Service directory. Into these we put the compiled interface and service SWF files (or you can have the ANT script do that for you).

image

The Interface Library Entry

In the catalog’s descriptor.xml file, there needs to be an entry for the interface library. The library has a name that will serve as a reference to other entries.

My advice is that you name the library the same as the interface library’s Flex project and SWF file name. There will be a lot of references in the catalog after we’re done and keeping the names strait will be messy enough.

For example, my IDogData interface is in a Flex project called MyDataInterfaceLibrary, so the interface library entry in the catalog looks like this:

<tile:InterfaceLibraryList>

<tile:InterfaceLibrary name=”MyDataInterfaceLibrary“>

<crx:Metadata>

<crx:Description>Dog Data Interfaces</crx:Description>

</crx:Metadata>

<tile:InterfaceList>

<tile:Interface interfaceName=”com.adobe.etech.IDogData“/>

</tile:InterfaceList>

</tile:InterfaceLibrary>

</tile:InterfaceLibraryList>

The Service Library Entry

The service implementation class needs an entry in the catalog as well. The service entry refers to the Interface Library Entry.

It also allows us to set some data when the service instance is first created. When I created the DogData class, I added a constructor that took four parameters. In the Service Library entry in the catalog, I use the ConstructorArgs tag to load some initial data into the instantiated object. That way I’ll have some dog data when the tiles first go to use the object.

<tile:ServiceLibrary name=”MyDataService“>

<crx:Metadata>

<crx:Description>Dog Data service</crx:Description>

</crx:Metadata>

<tile:ServiceClassList>

<tile:ServiceClass className=”com.adobe.etech.impl.DogData name=”SampleDogData scope=”singleton“>

<tile:Implements>

<tile:Interface library=”MyDataInterfaceLibrary interfaceName=”com.adobe.etech.IDogData“/>

</tile:Implements>

<tile:ConstructorArgs>

<tile:Argument value=”Rover“/>

<tile:Argument value=”Brown“/>

<tile:Argument value=”Doberman“/>

<tile:Argument value=”true“/>

</tile:ConstructorArgs>

</tile:ServiceClass>

</tile:ServiceClassList>

One other important thing to note is the scope attribute. By setting its value to singleton, I’m telling Mosaic to create only one instance of that object. All tiles will share that one instance. If anything changes it, it will be changed for ALL tiles.

The Tile Entries

The tile class entries need to be modified to tell Mosaic how to map the interface library types to variables in the tile. This is done by adding a Depends and Property element to the TileClass element.

<tile:TileClass name=”AddDogs label=”AddDogs catalog=”SimpleServiceCatalog initialWidth=”400 initialHeight=”400“>

<crx:Metadata>

<crx:Description><![CDATA[This tile has a description. It can me plain text or contain HTML.]]></crx:Description>

<crx:Category>Dogs</crx:Category>

</crx:Metadata>

<tile:Depends>

<tile:Interface library=”MyDataInterfaceLibrary interfaceName=”com.adobe.etech.IDogData“/>

</tile:Depends>

<tile:Properties>

<tile:Property type=”com.adobe.etech.IDogData name=”dogData“/>

</tile:Properties>

<tile:Content uri=”/mosaic/catalogs/SimpleServiceCatalog/tiles/AddDogs/content contentType=”application/x-shockwave-flash loadAs=”default“/>

</tile:TileClass>

The Depends clause tells us that this tile uses interfaces from the specified interface library. The Property element tells us that the interface maps to a specific variable in the tile. If you look at the tile’s MXML code you will see a variable that corresponds to the name attribute. In this case there is the line:
[Bindable] public var dogData:IDogData;

So this maps the interface library to the content of the tile, note that it does not mention the service library. This is done in the application’s descriptor.

Putting it all Together with the Application Descriptor

The final step is to use the application descriptor to identify the exact service that will carry out the work of the interface. A properties element will be added to each tile reference that uses the service. This will map the service to the property. When combined with the catalog entry for the tile, this will inject the service into the tile’s variable.

For example:

<tile:TileReference name=”AddDogs label=”Add Dogs catalog=”SimpleServiceCatalog“>

<tile:Properties>

<tile:Property name=”dogData“>

<tile:ServiceReference catalog=”SimpleServiceCatalog library=”MyDataService name=”SampleDogData“/>

</tile:Property>

</tile:Properties>

</tile:TileReference>

This entry tells Mosaic to map the SampleDogData service to the dogData property in the AddDogs tile. If you look at the catalog entry for that tile you can see that the dogData parameter is an IDogData object. You can also see that the SampleDogData service is a DogData class with some pre-named values supplied to the constructor.

What’s it look like

Assuming all of the code, packaging and mapping was done right; the application should be deployable now. Once that’s done you should be able to run it by going to the application’s URL. In the example the application is called SimpleService. It should look like the following:

image

You can see that there are two tiles: the AddDog on the left and the ShowDogs on the right. One thing to note is that the ShowDogs tile has data to start. This is because the initial data was listed in the catalog along with the service entry.

If I make changes to the dog’s data using the AddDog tile and hit the Add button then the data in the ShowDogs tile will change as well.

image

This is because the Add button click handler sets a Mosaic attribute that includes the IDogData object. The ShowDogs tile on the other hand is watching for a message that includes an IDogData object. If I loaded a new tile that used the same service I would see the new data, this is because service was marked as a singleton.