Home >
Last installment we looked at searching for data with our application. Now we'll conclude our look at Flex, LiveCycle Data Services, and EJB 3.0 by exploring the administrative section of the application.
Administration Section: Layout and Navigation
The administration section is only a little more complex (see Figure 25).
There is an ApplicationControlBar along the top which is the main navigation for the admin section. The control bar has a ToggleButtonBar which is bound to the ViewStack, which makes the button bar have as many buttons as the stack has views, using the labels of the views as the labels of the buttons.
The ApplicationControlBar also has a label on the right side which is bound to the AdminModel’s outstandingReservations property, with a little extra text. The outstandingReservations property tells how many new reservations have come in and not yet been checked out, meaning that someone has reserved some books but has not picked them up yet. The administrator should get those books ready for checkout. We’ll see how this property is set later.
The three views of the view stack are the book management section, the outstanding reservations section, and the checked out reservation section. The management section is where People (Authors and Users), Subjects, and Books are created, updated, and deleted. The outstanding reservations section shows a list of reservations that haven’t yet been checked out, and the checked out reservations section shows a list of books that are checked out, allowing the administrator to check them in.
Administration Section: Data Management in Action
Let’s look at how the CRUD views work. EditBooksPanel includes three edit panels: BookEdit, SubjectEdit, and AuthorEdit. There is some interaction between the three and the EditBooksPanel when a Subject or Author is selected. An event is emitted from the SubjectEdit and AuthorEdit components when one of these is selected, caught by the EditBooksPanel, and set as the current Author or Subject for editing or creating a new Book.
What’s more interesting, though, is editing one of these three. First, instead of a separate form to edit, I’ve used a DataGrid in editable mode. For this simple use case, that works nicely. For a more complex use case, we would probably need a form. Here’s a look at SubjectEdit:
<mx:Panel ... creationComplete="init()">
...
<mx:Script>
[Bindable]
private var model:AdminModel = AdminModel.getInstance();
public function get selectedSubject():Subject {
return subjectGrid.selectedItem as Subject;
}
private function init():void {
new GetAllSubjectsEvent().dispatch();
}
private function subjectSelected():void {
dispatchEvent(new Event("subjectSelected"));
}
private function removeSubject():void {
new DeleteSubjectEvent(subjectGrid.selectedItem as Subject).dispatch();
}
private function createNewSubject():void {
var subject:Subject = new Subject();
subject.name = "New Subject";
model.subjects.addItem(subject);
}
</mx:Script>
The DataGrid is populated with the right data on creationComplete, which is when the view is ready for display, by firing the GetAllSubjects event, this time in the admin package.
<mx:DataGrid
id="subjectGrid"
dataProvider="{model.subjects}"
click="subjectSelected()"
editable="true"
width="100%"
height="100%"
>
<mx:columns>
<mx:DataGridColumn
dataField="id"
headerText="Id"
editable="false"
/>
<mx:DataGridColumn
dataField="name"
headerText="Subject Name"
/>
<mx:DataGridColumn
dataField="createdOn"
headerText="Created"
editable="false"
/>
<mx:DataGridColumn
dataField="updatedOn"
headerText="Updated"
editable="false"
/>
</mx:columns>
</mx:DataGrid>
<mx:HBox>
<mx:Button label="New" click="createNewSubject()" />
<mx:Button label="Remove" click="removeSubject()"
enabled="{subjectGrid.selectedItem != null}" />
</mx:HBox>
But look at the code to edit subjects—if you can find it, that is. You’d think you’d need to capture when a row was edited or added, get the information, and send it to the service for persistence, right? We’ll, you’d be right in that it needs to be done, but what’s actually doing all of that work are the data management features of LCDS. All we need to do is make the changes to the data already in the managed collection, or add or delete a row from that collection.
When the init method loaded the data, it got a collection from the data service and put that on the model, and the grid is bound to that model’s collection. Because the DTOs in the collection are all marked as managed:
...
[Managed]
[RemoteClass(alias="lcds.examples.bookie.entity.Subject")]
public class Subject extends BaseEntity {
...
and the DataService is auto-syncing (the default state), the LCDS code on the client knows to automatically save any changes to those DTOs.
This should strike you as an incredible aid to the development effort. That glue code to hook up the edits to any particular object all the way through to the service layer is written for you already—you don’t even have to think about it.
Explore the sample code more if you want, but the important “take home” is how the data management features work hard so that you don’t have to.
Administration Section: The Reservation Alert Notification
One last item of note in Bookie is the notification when a new reservation is received. We’ve seen how the topic is set up in JBoss, and we’ve seen how the ReservationsDAOBean publishes a message to that topic. Now let’s look at how Flex signs up to get those messages.
First, in the Cairngorm services definition, lcds.examples.bookie.business.Services, there’s a definition for a Consumer called reservationConsumer:
<mx:Consumer
id="reservationConsumer"
destination="outstandingReservationTopic" />
A Consumer is LCDS’s class for subscribing to and receiving messages. Our reservationConsumer is set to a destination called outstandingReservationTopic, defined in messaging-config.xml:
<destination id="outstandingReservationTopic">
<properties>
<network>
<session-timeout>0</session-timeout>
</network>
<jms>
<destination-type>Topic</destination-type>
<message-type>javax.jms.ObjectMessage</message-type>
<connection-factory>
TopicConnectionFactory
</connection-factory>
<destination-jndi-name>
topic/outstandingReservationTopic
</destination-jndi-name>
<destination-name>
outstandingReservationTopic
</destination-name>
<delivery-mode>NON_PERSISTENT</delivery-mode>
<message-priority>DEFAULT_PRIORITY</message-priority>
<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
<transacted-sessions>false</transacted-sessions>
</jms>
</properties>
</destination>
This destination points to the topic and connection factory and sets the properties of the topic that LCDS needs to know. All we need to do to start listening for messages is to tell the consumer to subscribe. What I’ve done is create something similar to a wrapper to the Consumer in lcds.examples.bookie.business.NotificationManager. It takes a Consumer in the reservationConsumer setter, subscribes that Consumer (if it wasn’t already), and starts listening for messages.
package lcds.examples.bookie.business {
...
public class NotificationManager {
...
private var _reservationNotificationView:NewReservationNotification;
public function set reservationNotificationView(view:NewReservationNotification):void {
_reservationNotificationView = view;
}
private var _consumer:Consumer;
public function set reservationConsumer(consumer:Consumer):void {
if (!consumer.subscribed) consumer.subscribe();
consumer.addEventListener(MessageEvent.MESSAGE, onNewReservation);
_consumer = consumer;
}
...
public function onNewReservation(me:MessageEvent):void {
var reservation:Reservation = Reservation(me.message.body);
new ReservationRecievedEvent(reservation).dispatch();
notifyOfReservation(reservation);
}
public function notifyOfReservation(reservation:Reservation):void {
_reservationNotificationView.show(reservation);
}
}
NotificationManager is a singleton because we should have only one. To bootstrap this NotificationManager, admin.mxml fires a command when it’s initialized, called lcds.examples.bookie.command.admin.ReservationNotificationSubscribeCommand, which simply sets the consumer on the NotificationManager. It also gets an instance of the NewReservationNotification component, which displays a notification when a new reservation is received.
package lcds.examples.bookie.command.admin {
public class ReservationNotificationSubscribeCommand
implements ICommand {
public function execute(event:CairngormEvent):void {
var evt:ReservationNotificationSubscribeEvent =
event as ReservationNotificationSubscribeEvent;
var consumer:Consumer =
EnterpriseServiceLocator.
getInstance().
getConsumer("reservationConsumer");
var manager:NotificationManager =
NotificationManager.getInstance();
manager.reservationConsumer = consumer;
manager.reservationNotificationView =
evt.reservationNotificiation;
}
}
}
Once a message is received, NotificationManager.onNewReservation is called, and that fires an event which corresponds to the lcds.examples.bookie.command.admin.ReservationReceivedCommand. The NotificationManager then tells the NewReservationNotification to show itself. That view is a simple HBox that shows a collection of books in a reservation, and then animates itself away similar to how the Thunderbird email client shows that you have new email (see Figure 26).
Here’s the code for that little widget:
...
private var hideTimer:Timer;
public function show(reservation:Reservation):void {
reservationTitles.text =
reservationTitles.text +
reservation.book.title + "\n";
reservationTitles.maxWidth = this.width;
if (hideTimer == null || !hideTimer.running) {
hideTimer = new Timer(3000, 1);
hideTimer.addEventListener(
TimerEvent.TIMER_COMPLETE, hide);
} else {
hideTimer.reset();
}
visible = true;
}
public function showEffectComplete():void {
if (hideTimer != null) hideTimer.start();
}
public function hide(event:TimerEvent):void {
visible = false;
}
public function hideEffectComplete():void {
reservationTitles.text = "";
hideTimer = null;
}
</mx:Script>
<mx:hideEffect>{hideDissolve}</mx:hideEffect>
<mx:showEffect>{showWipeDown}</mx:showEffect>
<mx:WipeDown id="showWipeDown" effectEnd="showEffectComplete()"
target="{this}" />
<mx:Dissolve id="hideDissolve" effectEnd="hideEffectComplete()" target="{this}" />
<mx:Label id="titleLabel" text="New Book Reservations" fontWeight="bold" />
<mx:Text id="reservationTitles" />
...
To test this, deploy the application and open both the user application and the admin application side by side (see Figure 27).
Browse for a few books and reserve them from the user side, and watch the notification come up on the admin side. Slick. And easy!
You can always find the entire series here.


























Hi Tony,
Just wanted to say that these articles, as good as they are, should all be upgraded to Flex 3.0. just have a look on all the comments people left on the 2nd entry... I think most of them stopped there. If the example code doesn't work... well, you know...
@Flexi - I totally agree, it's just an issue of timing. This "article", which is actually a serialization of an O'Reilly Shortcut, was written almost a year ago and serialized on this site. I haven't actually been writing these fresh each week.
I'd love to upgrade it, but it's an issue of time. I'll bring up your concerns, and thanks for reading until the end :)
No no... thank you, mate!
I figured that that was the issue - time :)... well, no pain no gain, hmm?
Tony,
These articles are good but have basically nothing to do with enterprise flex, the title is very misleading.
Enterprise is not the same as somewhat large app and most of these patterns wouldn't apply to real enterprise systems. I am not sure where these ideas were confused along the way (in many developers heads) but the word enterprise means something and its casual use is making it harder for those of us trying to find real information on enterprise flex to actually find anything at all.
Pete
@Pete
What I meant by Enterprise was connecting to and loading data from a Java Enterprise Application Server, such as JBoss, and the development tasks that work with that environment.
As a dissenter, can you give us an idea of what you would have liked to have seen in this shortcut instead of or in addition to what's there?
Tony
@Pete
Please do share with is what you mean by your "Enterprise" complaint. These articles show you the in and outs of building a real application that connects with and uses real backend services. Moreover, it provides tons of great insight on how/which frameworks to be using in each of the various application layers. What more could you possibly want.
Great articles!