Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
2
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
MEAP Edition Manning Early Access Program
Copyright 2009 Manning Publications
For more information on this and other Manning titles go to www.manning.com
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
3
Part 1: Getting started 1. Some Flex with your Java 2. Beginning with Java 3. Getting Rich with Flex 4. Connecting to Web Services Part 2: Strengthening the backend 5. BlazeDS remoting and logging 6. Flex messenging Part 3: Going above and beyond 7. Securing and personalizing your application 8. Charting with DeGrafa 9. Desktop 2.0 with Adobe AIR 10. Testing your Flex app with FlexUnit 11. Flex on Grails
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
4
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
1 Some Flex with your Java?
This chapter covers:
The basics of Flex
The components we utilize alongside Flex and Java
In 1995, Sun introduced the first Java platform and with it birthed the “applet.” The applet allowed Java applications to run inside the browser with rich functionality and all the benefits of the Java framework, including connecting to the server side. The applet became hugely popular for a couple of years but then its popularity waned. The applet adoption rate dropped mainly because of problems surrounding the browser plugin. Building features in an applet from scratch or even other rich implementations can be expensive compared to the simplicity of using the Flex framework. Figure 1.1 displays a simple Java Applet datagrid next to a Flex datagrid. The Flex datagrid not only looks much nicer out of the box than the applet; it's also much more functional. The Flex DataGrid and AdvancedDataGrid components provide built-in support for such tasks as sorting, dragging columns, row highlighting, data nesting, and styling.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
5
Figure 1.1 Comparing a Java applet DataGrid to a Flex Advanced DataGrid
Macromedia embraced the idea of having a dedicated runtime environment for the browser and in the year 1997 debuted the Flash Player. The Flash runtime provides lightweight graphics and animation capabilities in manageable file sizes. This has made the player hugely successful across operating systems and browser platforms. The Flash runtime allows for Rich applications to have true stateful experiences and a high level of security.
A STATEFUL FLEX EXPERIENCE A stateful experience with Flex means that the client (Flex) will manage or remember everything it needs to without needing to submit to the server side, update and manage a session or request through Hyper Text Protocol (HTTP), and then refresh the client side with updated data after a submit with data from the session or request.
In general, Java developers have done extremely well in leveraging the principles of Object Oriented Programming (OOP) to build extremely stable, testable, and extensible applications. Flex has become a rich internet application (RIA) solution for Java developers because it not only bridges the gap between a solid server side and a great user interface; it is built on top of solid OOP principles such as abstraction, encapsulation, inheritance, and polymorphism. These advantages benefit other technologies besides pure Java.
As you'll see in (the
bonus) Chapter 11, when we demonstrate Flex integration with one of the hottest new frameworks for web development, Groovy on Grails. We'll work on a bug tracking system to complement other sample applications throughout the book. Integrating Flex with Grails is just as easy as with Java and, in many respects, even easier. Flex development is now bolstered with many of the benefits of Java-like frameworks for performing unit testing, continuous integration, and agile development practices. The ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
6
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
combination of frameworks and the Flex SDK allows even beginning developers to begin developing applications immediately. A thriving Flex open source community can offer Java developers components as simple as a custom navigation tree or button and as complex as a datagrid that allows a user to sift through items in an iTunes CoverFlow visual format. Most of these custom components simply extend stock Flex objects found in the SDK. Adobe made Flex 3.0 open source and it now has a ton of community resources available. It’s worth mentioning that the FlexSDK with Adobe’s built-in charting components is still commercial for some reason. We have chosen to show you Flex for Java because of the powerful duality of a rich and stateful client in conjunction with a powerful server side. Also, Java is broadly used in the mainstream and is the existing server-side for many Flex migration projects. While there are currently other alternative ways for doing RIA development, Flex will most likely prove to be a champion RIA framework because of the simplicity and testability it provides to developers. We are now ready to discuss some of Flex framework features.
1.1 A whirlwind tour of Flex It’s time to take a peek at the components we’ll use throughout this book. We won’t go into too much detail about the components as that is beyond the scope of this book. Instead, we will focus on the usage of components and framework in real world development.
1.1.1 MXML and ActionScript At the heart of every Flex application you’ll find a combination of MXML files (XML files with the .mxml extention) and ActionScript classes. They are the basic building blocks of the Flex framework.
The Flex compiler takes these files and creates a SWF file which is then
executed in the Flash Player. MXML MXML is a XML based markup language similar to HTML/XHTML. The MXML syntax is used to declaratively define your application. MXML has numerous tags for common user interface objects, such as text input fields, radio buttons, and dropdown lists. It also has many user interface components and layout components that are common to rich client development, such as menu bars, tabbed panels, data grids, and navigational trees. In addition to these controls it's possible to build custom components that extend existing ones or do completely something different. For instance, in chapter 8 we will be covering the Degrapha drawing API to create a pie chart for a samle application. ACTIONSCRIPT ActionScript, and more specifically ActionScript 3.0, is a dynamic scripting language based on the ECMAScript Language Specification, Third Edition. It is composed of two parts: the language specification itself and the Flash Player API. It is very similar to JavaScript in syntax, so it should look very familiar to any experienced web developer. Unlike JavaScript, ActionScript is actually compiled into byte code before being executed instead of being parsed and interpreted at runtime. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
7
UTILIZING BUILT-IN ACTIONSCRIPT CONTROLS Dozens of user controls, powered by ActionScript, are available with Flex out of the box. As we demonstrate later in this book, existing components can be extended to create your own custom components.
Since your application will always run inside the Flash
Player, you don’t have to worry about cross-browser compatibility issues either. In Chapter 8 we will go over how to utilize custom components in your Flex applications using ActionScript for the purpose of reuse.
ActionScript is a dynamically typed language similar to Python or Groovy and does its type checking at runtime instead of at compile time. You have the option of directing the compiler to perform type checking at compile time by enabling strict mode on the compiler, but this is not a good substitute for a comprehensive set of unit tests. Flex Software Development Kit (FlexSDK) and Flash Player are the two key elements in making a Flex application come to life. We will now briefly introduce those elements along with BlazeDS. Now comes the part we’ve all been waiting patiently for—we’re going to create a “Hello World” application in Flex. CREATING AN APPLICATION IN FLEX Let's start by modeling our directory structure, shown in Figure 1.2, after the Maven default project structure. This will prove useful since we are using Maven to build the Flex-Bugs sample application in chapter 2. The sample application can be placed in a project directory such as C:\dev\projects for Windows, or /home/user/dev/projects Linux.
Figure 1.2 The folder structure for our “Hello World” application is formatted for the Maven build convention.
The main source code location for our hello world sample application will be contained in the src/main/flex folder. Since ActionScript follows a similar pattern for packages as Java, if ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
8
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
your ActionScript class belongs to the com.example package, the source for this class will be contained in the src/main/flex/com/example folder. The src/main/resources folder should contain any resources that belong to the application but are not compiled with the sources. For example, any configuration files or message bundles belong in the resources folder. The src/test/flex
and
src/test/resources
folders
are
identical
to
the
src/main/flex
and
src/main/resources folders respectively, the only difference being that these folders are for the test code of the application. In true “Hello World” fashion, we are going to start simple and create the simplest Flex application possible in Chapter 3. We'll expand on that and produce a Flex client application that interacts with a Java web application server-side. For the purpose of introducing some Flex code, the following snippet demonstrates a very trivial example of a simple Flex application. We are going to create a single .mxml file that will print out the words “Hello World”.
#1 #2 #3
#1 XML document declaration #2 MXML application root node #3 MXML Text element
If you’ve done any web development before, this should at least look somewhat familiar to you. MXML, just like XHTML is nothing more than XML. At the beginning of the file you’ll see the standard XML declaration (#1). Immediately following this you’ll see the root node (#2), which in most Flex applications will be the element. In chapter 3, we will greatly evolve this example by creating a nice Flex application. In Chapter 9, where we talk more about Adobe AIR, the root node for an AIR application is typically
. Inside of the element is a single element (#3) with its text attribute set to “Hello World!”. In order to see the wonderful things you can do in Flex, including the beloved Hello World application, there must be a way to build the application. Now that we’ve created the “Hello World” application, let’s discuss our build options.
1.1.2 The FlexSDK The FlexSDK comes in two flavors: the Free Adobe Flex SDK and the Open Source Flex SDK. Both contain everything you need in the way of developing, optimizing, and debugging Flex applications. It includes the ActionScript and MXML compilers, tools for creating JavaDoc-like documentation, and the Flex Framework. The only real difference is that the Free Adobe Flex SDK contains some additional components that enhance the Flex application, such as tools for advanced font encoding, tools for packaging AIR applications, and the Flash Player. These extra components are not open source but have been made freely available by Adobe. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
9
1.1.3 BlazeDS Up until fairly recently if you wanted to connect your data driven application to a Java based backend, your choices were fairly limited. You could expose your Java services as XML Web Services, either as SOAP based or RESTful and connect to them this way, write your own custom marshaller/unmarshaller based on the Adobe AMF protocol, or pony up big bucks for a license for Adobe’s LiveCycle Data Services. With the release of Flex 3, however, Adobe decided to spawn an open source project named BlazeDS, which contains much of the functionality specific to connecting Flex to a Java based service.
In Chapter 4, we cover
more about how to connect to Java using both web services and the BlazeDS components.
1.1.4 Flash Player 10 Of course none of this could be possible without the Flash Player 10 runtime. It is the heart and soul of every Flex application. While the Flash Player itself is not open source, it has been available free of charge since its inception.
The Flash Player can be found on
nearly every computer in the world. The Flash Player gives your Flex applications the ability to execute and look the same no matter what browser your application runs in.
Because
your Flex application runs inside the Flash Player, you do not have to be concerned with cross browser issues.
Adobe Contributes ActionScript engine source to Mozilla In November of 2006, Adobe contributed the source code for their ActionScript Virtual Machine to the Mozilla foundation, thus spawning the Tamarin project. Tamarin will in the future support ECMAScript Edtion 4 and be integrated into the SpiderMonkey project, Mozilla’s next generation JavaScripting engine to be included with future versions of Mozilla (Mozilla 2008).
Because of the widely popular Flash Player and a powerful open source SDK, Flex is a great fit for Java developers needing to build rich clients. Now let's take a peek at how we'll utilize Flex in this book.
1.2 Inside Flex on Java This book is primarily for any developer who would like to learn how to integrate Flex on Java. With that in mind, we looked for the perfect ingredients to equip its readers with the right tools needed in real world scenarios without overcomplicating things. We took a more purist approach to development and used good design patterns and didn't introduce any MVC or other event-based frameworks to help us with that. The point being, if our readers learn how to devlop in this way, using the inherit event-based Flex API and framework, they will be able to integrate pretty much any framework out there with intuity and better understand when is the right time to do so. Our focus will also not be on teaching Java. Even though we will build a Java web application in chapter 2 the intent of this book is not to teach our readers Java. In fact, we ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
10
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
went to great lengths to figure out how to avoid as much Java learning curve as possible and still allow the readers that are new to Java to build a simple Java web application. That's why we chose a good framework that will help us with that. Flex on Java will provide readers with powerful examples in how to integrate the two technologies. Since, it's Flex on Java there's more of a focus on Flex itself and is concerned with the integration with Java. Therefore, we will be tackling the following topics in detail.
1.2.1 Beginning with Java In chapter 2 we begin by laying a foundation by rapidly building a sample Java web application to work with. This will be useful if you don't already have an application or need a little assistance in getting started with setting up a Flex on Java development environment. Setup type chapters can feel slightly mechanical because of the downloads and installations they must ramble through. However, we've tried to keep it interesting and painless while using some popular development environment frameworks that majority of the readers will be pleased to see being utilized. With the big picture in mind we carefully chose the Java technologies used in chapter 2. Most Java developers will feel at home while developers new to Java shouldn't feel overwhelmed and can also download the full sample application.
1.2.2 Building a Flex Interface Chapter 3 will demonstrate building a disconnected Flex client. We will be creating the beginnings of a rich user interface for the Flex-Bugs sample application shown in figure 1.3.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
11
Figure 1.3 The Flex-Bugs sample Flex application
A disconnected Flex client can take advantage of mock data and can allow developers to easily prototype the user interface without the complexity of external dependencies.
1.2.3 Integrating with Web Services Integrating Flex with the server side is what this book is all about. It allows us to go from mock to real inside a sample Flex client. In chapter 4 we will demonstrate connecting to the Java server side and how to leverage the powerful Flex API for connecting to web services. What readers may find different in this chapter is that we will actually connect to the server side through the Model-View-Presenter (MVP) design pattern instead of the typical approach of simply using the mxml tag element approach that doesn't scale well for most applications. Now that this chapter is going to connect to the server-side services we will be covering event dispatching and handling in detail. Interfaces and views will also be covered as we design a clean, well designed Flex client, that will scale on demand.
1.2.4 Integration with BlazeDS, Logging and Messaging Integrating Flex with Java is what this book is all about. That's why in chapter 5 will demonstrate further how to connect a Flex client more directly to the server-side using the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
12
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
opensource BlazeDS framework. BlazeDS provides a mechanism for allowing Flex to call methods on Java objects through binary serialization. This is much faster than what's possible with Web Services or XML/HTTP(s) because it's using real objects and doesn't have to marshal XML. Since logging is a critical component to any application and development environment, chapter 5 also covers BlazeDS logging in detail. Real-time messaging is an important feature in most enterprise applications. Chapter 6 will also demonstrate how to develop Flex applications that take advantage of simple polling and JMS in chapter 11. The Flex framework provides an API that enables distributed communication with the server-side or between clients that is loosely coupled and asynchronous. Flex has both a powerful and simple API for handling messaging.
1.2.5 Securing Flex Applications Security is something that seems to be missing in most technical books for some reason. In a utopian society we wouldn't need it but it's always the elephant in the room when gathering requirements for an application. However, since we don't live in utopia we better understand how it works in Flex. If not handled with care, security can be one of the most expensive features in a
business application. Chapter 7 will demonstrate building
authentication, authorization, and personalization with Spring Security (Acegi) integration.
1.2.6 Creating custom Flex controls with DeGrafa One of the most powerful features of Flex has to do with what is not in the SDK. Confused? In chapter 8 we will explore creating and leveraging custom Flex controls with the open source framework DeGrafa. DeGrafa is a declarative graphics framework that provides a suite of graphics classes. DeGrafa can be used to build robust charts, tools, and other graphical elements with less effort and complexity. In chapter 8 will create a custom pie chart component that will be appropriately tied into our sample application in good taste. We will also demonstrate creating a DataGrid ItemRenderer and perform dynamic object creation.
1.2.7 Desktop 2.0 with Adobe AIR A Business sometimes can't live with the web alone. For times when a desktop client is the best way to go Flex goes beyond the web with Adobe AIR. A Flex application can easily be ported to a desktop environment with AIR. This is especially easy for applications built with a good design. Chapter 9 will demonstrate how to allow a Flex application to live in two worlds, creating a key and signing a Flex/AIR application, and packaging and distributing an AIR application.
1.2.8 Flex on Grails Finally, chapter 11 will cover Flex integration with Groovy and Grails. There we will build a standalone application end-to-end demonstrating the power of Flex and Grails development together. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
13
The Groovy programming language is the first dynamic scripting language to be adopted as a standard through the Java Community Process (JCP). The specification for Groovy can be
found
under
Java
Specification
Request
(JSR)
241
on
the
web
at
http://jcp.org/en/jsr/detail?id=241. While Groovy supports powerful features that can be found in others like Python, Ruby and Smalltalk, Grails (a groovy DSL) provides a rapid development environment for both server-side and the web that rivals the Ruby on Rails (RoR) framework. With Flex on Grails you can quickly create a dynamic application that runs on the JVM with much less effort.
1.3 Summary Whether you are new to building web or desktop applications with Flex on Java or an experienced developer, this book will teach you how to integrate Flex on Java quickly and effectively. In this chapter we introduced the basic elements of Flex and the additional components we'll utilize throughout the book, but in the next chapter we'll focus on building the Java side of our application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
14
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
2 Beginning with Java
This introduction covers
Generating the application structure with Maven
Building some Java server-side domain objects and services
Building a simple JSP user interface
Now that we've introduced you to the basics of Flex and the additional components we'll utilize, we will begin by creating a Java web application. The Java application will expose web services so that we can later connect to them from a Flex client. We have attempted to not tie the book to a specific sample application by focusing more on the concepts and techniques around using various frameworks and tools. This should allow you to pick a topic in the book that interests you and get rolling on it. However, we will demonstrate many topics by using a application built in this chapter called “Flex-bugs”. So, if you want to follow the samples of in the book you can download the full code listings on the book's website at http://manning.com/allmon. You could also replace the application contents with something else that's more meaningful to you by changing the domain objects to manage whatever you want like contacts, movie favorites, etc. Feel free to play with the code snippets all you want! Lean into the big picture concepts and techniques we hope will help you successfully integrate Flex with Java. Throughout the book, especially in this chapter, we leverage a few Java frameworks that help to lighten the amount of work there is in building a fully functional web application. This chapter is a little mechanical in nature because we need to set up a development environment. So, there are a few downloads and installs that must take place if you choose to use our samples. You may find that you can just browse through this chapter and do only what you haven't done already before moving on.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
15
The Java frameworks used will help to keep development to a minimum for creating a sample application to work with for integration purposes. This will allow us to focus on teaching and demonstrating how to build synergy between Flex and Java.
2.1 Working with Appfuse In order to have a Java application to work with from chapter 3 on, we decided to build that first. However, you can start with Flex in chapter 3 if you'd like or move around the book as you find it convenient. We decided to do build the Java application first since the largest percentage of readers will be refactoring existing applications to include a Flex client and this will give you something to play with fast. In this chapter we'll start off by generating the project structure with the Apache Maven, a convention-over-configuration project management framework. Maven will build the application for us and speed up the development process for us. Once we have a project structure generated, we'll start building the server-side components while leveraging MySQL for the database. As for the Java server-side pieces we will start with creating some Plain Old Java Objects (POJOs), some Data Access Objects (DAOs), and some service objects that will be exposed to a Web tier. Before we begin writing a Flex client that integrates with Java we'll first setup a simple Java server side application using the AppFuse framework. AppFuse was created by Mark Raible of Raible Designs for the purpose of simplifying the construction of Java web applications through convention. Using AppFuse on the server side will allow us to focus on the integration of Flex with Java creating simple domain and service Java objects. Since the layers of architecture and complexity can make approaching the building of a Java web application a bit daunting AppFuse is a great technology choice. For those not familiar with building Java web applications Appfuse will simplify the task of dealing with the layers and delivering value faster. AppFuse allows a Java developer to quickly start focusing on business domain concerns. A typical Java application will be POJO driven and wired together through the open source dependency injection (DI) framework Spring. The dependency injection design pattern helps to build applications with loosely coupled components making your application more flexible and testable. The following steps will guide you through setting up your development environment with the AppFuse framework. Finally, Appfuse comes stocked with Maven integration to make things even easier and that's where our development set up begins. So, let's get things rolling by installing Maven.
2.2 Generate the application structure with Maven To pigeon hole Maven and call it a build system really doesn’t do it justice. Apache Maven is much more than just a build system; it is described as a software project management and comprehension tool. So what exactly does that mean? At the core of every Maven project is ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
16
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
a project object model, or more affectionately known as the POM, and from this POM Maven can build, generate reports, generate documentation, and more, all from a single description of the project. If you’d like to learn more about Maven you can either check out the Apache Maven project site at http://maven.apache.org or download the free e-book from Sonatype at http://www.sonatype.com/book. Before we get going with Maven we need to be sure we first have the Java Development Kit (JDK) properly installed. You can follow the next section for that or skip it if you're ready to go. After we install the JDK we will be sure to install the MySQL database as well. We will need MySQL installed before generating the project with the Appfuse Maven archetype.
2.2.1 Download and Install the Java Development Kit (JDK) In order to run any Java server side environment the JDK must be installed and configured.
Download
and
install
JDK
http://java.sun.com/javase/downloads/index.jsp.
1.6+ Set
from up
an
the
Sun
environment
website
at
variable
for
JAVA_HOME that points to the JDK directory. It's also helpful to add the JDK's bin directory to the path. Open up a command prompt and type in java –version, in order to verify that Java is installed correctly. The version information of the configured JDK should be presented as shown in figure 2.1.
Figure 2.1 Verify that Java is set up correctly by checking the version
Once Java is configured we can move on to setting up the open source MySQL database.
2.2.2 Download and Install MySQL In order to demonstrate database integration and persistence we will use MySQL. MySQL is an open source database that is extremely lightweight. Download and install MySQL 5.x from the MySQL website at http://dev.mysql.com/downloads/mysql/5.0.html#downloads. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
17
SET UP A DATABASE Here we will set up a database for the Flex-Bugs sample application. After you have MySQL installed pull up the command prompt and login to MySQL using the root account and then create the Flexbugs database as shown in figure 2.2. Using the command mysql -u root -p will instruct MySQL to login to the localhost instance of MySQL using the root account. It will then ask for the password. Please keep record of the admin account's user and password for later reference. Creating the database is as simple as executing the command create database flexbugs.
Figure 2.2 Using the MySQL commands to login into the database instance and create the Flexbugs database
Now let's move on to installing Maven for creating the project structure, managing the dependencies, building the application.
2.2.3 Download and Install Maven Maven can be downloaded at http://maven.apache.org/download.html. Be sure to download version 2.0.9 or above. Once Maven is downloaded it can be configured by simply setting up a M2_HOME environment variable that points to where Maven was installed. The M2_HOME/bin directory will need to be set onto the path as well or exported for any Unix platform. For more assistance on installing or configuring Maven visit the Maven website here http://maven.apache.org/download.html#Installation.
2.2.4 Create a Maven Multi-module Project We are going to create a Maven Multi-Module project called Flex-Bugs. A multi-module project could be configured manually by creating a top-level “super POM”, adding projects under the super POM directory, and editing the super POM to include the modules with the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
18
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
modules element. However, we are going to use a technique that exploits a little known feature of the archetype:create plugin, and the Maven site archetype to kickstart the project. Creating a multi-module project has many benefits, one of them being the ability to build every artifact in a project with one simple “mvn compile” command. Another benefit is that if you are using either the maven eclipse:eclipse plugin or the idea:idea plugin, you can enter this command at the root of the project and it will generate all of the project files for all of the contained modules. First we'll generate the top level project using the maven-archetype-site-simple archetype using the following command: mvn archetype:create -DgroupId=org.foj -DartifactId=flex-bugs -DarchetypeArtifactId=maven-archetype-site-simple This archetype generates a Maven project with the directory structure as shown in figure
2.3. Figure 2.3 The generated top-level maven project
The project that is generated is the minimum project setup to generate site documentation. The index.apt file is the main index page for the site, and is written in the Almost Plain Text format, which is a wiki like format. You can also generate a more complete site project using the maven-archetype-site archetype like this, mvn archetype:create -DgroupId=[Java:the project's group id] -DartifactId=[Java:the project's artifact id] -DarchetypeArtifactId=maven-archetype-site This will generate a project structure similar to figure 2.4.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
19
Figure 2.4 A fully dressed up maven site project
After you have generated the site project, edit the pom.xml created from the site archetype plugin. Make sure the packaging type is set to “pom” like in listing 2.1. We've left stuff out for brevity.
Listing 2.1 Packaging of type “pom” indicates a multi-module project. 4.0.0 org.foj flex-bugs 1.0-SNAPSHOT pom ...
#1
Cueballs in code and text #1 Artifact type (jar, war, ear, ect.)
By setting the packaging type to “pom” (#1), any projects you generate from the root of the project directory will insert itself into the project by creating an entry into the modules section of the pom.xml for the site. Appfuse comes stocked with custom Maven archetypes. The different archetypes allow Appfuse to create different flavors of Java web applications with varying technology stacks. We will use the Struts 2 Basic archetype for the flexbugs sample application. In the root directory of your project that you created above, type in the command in listing 2.2.
Listing 2.2 Create the flex-bugs-web module for the java server-side mvn archetype:create ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
20
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
-DarchetypeGroupId=org.appfuse.archetypes -DarchetypeArtifactId=appfuse-basic-struts -DremoteRepositories=http://static.appfuse.org/releases -DarchetypeVersion=2.0.2 -DgroupId=org.foj.flex-bugs -DartifactId=flex-bugs-web
#1 #2 #3 #4 #5 #6
The appfuse-basic-struts (#2) archetype isn't a built-in maven resource. Instead, it's provided through a remote repository (#3). You provide maven with coordinates to the archetype by also providing the archetypeGroupId (#1) and version (#4) along with the rest of the required details. The groupId (#5) points to the top-level project and the artifactId (#6) is the name of the module you are about to create. Once you've executed the command, look inside the top-level pom.xml from the main project. There should now be an entry towards the bottom of the file like the following. ... flex-bugs-web ... Executing the command in listing 2.2 should generate the project structure shown in figure 2.5. Do not be concerned with the warnings while creating your project, they are expected. As long as you see BUILD SUCCESSFUL at the end, your project was created successfully.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
21
Figure 2.5 Generated module structure using the appfuse-basic-struts archetype
As you can see from figure 2.5 Maven was able to generate the project structure and even added in a couple files for us to test it out.
2.2.5 Maven provides a buildable Project If you look in the src/main/java/org/foj package you will find a source file called App.java. This Java class contains a single “hello world” method that will return the text “Hello” when invoked. Further down the project structure, in the test folder, you will see the AppTest.java junit test. This contains a single unit test that exercises the App.java class to ensure it returns hello. We can remove those files later. However, if you're new to Maven you may be wondering why this is significant and rightfully so. The answer will be found in executing the “mvn jetty:run-war” goal later and seeing what happens. The first thing to notice is that it appears to be building something. In fact, the flex-bugsweb POM tries to build a deployable Java web application archive or “war” but will first choke on a configuration
issue. If running the mvn jetty:run-war cmd without changing the
configuration you will most likely get this error. [INFO] ----------------------------------------------------------------------[ERROR] BUILD ERROR ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
22
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
[INFO] ----------------------------------------------------------------------[INFO] Error executing database operation: CLEAN_INSERT Embedded error: Access denied for user 'root'@'localhost' (using password: NO) [INFO] ----------------------------------------------------------------------[INFO] For more information, run Maven with the -e switch So, let's first edit the POM for the flex-bugs-web module. It will be located at the root of that module. There's a whole lot of stuff going on in there but we are going to focus on the piece we need to change. Clear down to the bottom we need to specify our MySQL user and password. Here's an example: ... root java4ever ... The maven archetype we used, brought to us by Appfuse, made it extremely easy to get to this point and is far easier than starting from scratch.
2.2.6 Running the Flex-Bugs-Web application Maven equips a developer with the ability to use the application immediately without manually deploying it anywhere. Executing the maven jetty:run-war goal from the FlexBugs-Web module will gather all the resources, compile all the code and tests, execute the unit tests, generate test reports, build a deployable war file, and launch the war file in an embedded instance of the very popular and lightweight Jetty servlet container. Using the appfuse-basic-struts archetype will also generate the default database for us and add some additional configuration files in order to allow developers to quickly get developing features. Once
you've
run
the
jetty:run-war
command
you
can
now
go
to
http://localhost:8080/flex-bugs-1.0-SNAPSHOT and login from there. By default, you can login to the application using “admin” for both the username and password. Once logged in you are redirected to the administration panel as seen in figure 2.6. From there you can do basic things like editing your user profile and managing users.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
23
Figure 2.6 Appfuse default application
There is nothing glamorous at this point in regards to the application. However everything you see and can do at this point has been with only a little setup effort. Appfuse does much under the covers for us from a framework and technology perspective. In fact, it's possible that it just saved us a week or more of typical Java development time when getting a project together with a little help from Maven. Before we start development of the Flex-Bugs sample application it's good to know that the source code is available for download at https://flexonjava.googlecode.com/svn/flexbugs/trunk.
2.3 Build the Model Objects A model object is a simple POJO that will be persistable and mapped to the database. Appfuse uses the Spring framework in conjunction with Hibernate to manage performing database operations for objects that are mapped to a database.
First let's start with Issue.java as seen in listing 2.3. For the flex-bugs application we need something to store Issues and Comments. An Issue is something that describes something that needs fixed to meet a requirement. This could be a bug, a new feature, a refactor, or an optimization for example. A single issue can have many comments so there will be a relationship built between the Issue and Comment objects.
Listing 2.3 The Issue model object ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
24
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
package org.foj.model;
# 1
import org.apache.commons.lang.builder.EqualsBuilder; ...
#2
@Entity public class Issue extends BaseObject implements Serializable {
#3 #4
private private private private private private private private private private private
Long id; String project; String description; String type; String severity; String status; String details; String reportedBy; Date reportedOn; String assignedTo; Double estimatedHours;
#5
@Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; }
#6 #7 #8
public void setId(Long id) { this.id = id; }
#9
... @Override public int hashCode() { return new HashCodeBuilder(11, 37).append(id).toHashCode(); } @Override public boolean equals(Object o) { if (null == o) return false; if (!(o instanceof Issue)) return false; if (this == o) return true;
# 10
# 11
Issue input = (Issue) o; return new EqualsBuilder() .append(this.getId(), input.getId()) .isEquals(); } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append(id) .append(project) .append(description)
# 12
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
25
.toString(); } }
Cueballs in code and text #1 Model Java Package #2 Import declarations #3 Java persistence framework #4 Issue extends Appfuse BaseObject #5 Class instance variables #6 Declares database pk relationship #7 Indicates how to generate id #8 “getter” method returns Id #9 “setter” method sets Id #10 hashcode to distinguish objects #11 equals to distinguish objects #12 toString provides object info
We will be storing the model objects in the org.foj.model Java package (#1) and will use the Appfuse framework in conjunction with the Spring Framework and Hibernate to simplify our application development. Spring provides a dependency injection and more while Hibernate provides a means to build persistable objects (#3) with an object-oriented design. The Id (#6) and GeneratedValue (#7) annotation help to facilitate the persistence by designating a field as a database primary key. The Issue object is a subclass of the Appfuse BaseObject (#4) and contains the instance variables (#5) we need to describe an Issue. All of the instance variables or “fields” have getters (#8) and setters (#9) required by the Java bean specification. Extending BaseObject requires us to override the toString (#13), equals (#12), and hashcode (#12) methods since they're defined as abstract in the BaseObject class. To implement these methods we are leveraging the Apache Commons Builder package (#2) for creating the elements for these methods. Any time you are implementing the Serializable interface, it's a good idea to also implement the equals and hashCode methods.
Constructing the application It's fair to say that we've formatted the book and code examples to be more consumable by the reader rather than verbosely describing the development process as it would have been performed. In reality, we constructed the application by building out the Issue object end-to-end and then added the Issue Comments after that. A user story driven approach to development reduces complexity, saves time, and increases productivity by helping to keep tasks simple by only focusing on one feature at a time. Task estimates for stories are smaller and more manageable and this helps to enable more visibility and agility in a project.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
26
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Next we will create a model object for a Comment. The comment, as seen in listing 2.4, will be another persistable object. There can be many comments to a single issue. For the remainder of the code snippets in chapter 1 we will use “...” for trivial things like imports and getters and setters of similar objects.
Listing 2.4 The Comment model object ... @Entity public class Comment extends BaseObject implements Serializable { private private private private private
#1
Long id; Issue issue; String author; Date createdDate; String commentText;
@Id @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; } public void setId(Long id) { this.id = id; } @ManyToOne(fetch = FetchType.EAGER) #2 public Issue getIssue() { return issue; } public void setIssue(Issue issue) { this.issue = issue; } ... @Override public int hashCode() { return new HashCodeBuilder(11, 37).append(id).toHashCode(); } @Override public boolean equals(Object o) { if (null == o) return false; if (!(o instanceof Issue)) return false; if (this == o) return true; Issue input = (Issue) o; return new EqualsBuilder() .append(this.getId(), input.getId()) .isEquals(); ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
27
} @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append(id) .toString(); } }
Cueballs in code and text #1 Comment declaration #2 Comment has a many-to-one relationship with Issue
There's not much different from an Issue POJO to a Comment (#1). The class fields are related to comments and there is a Many-to-one relationship (#2) with Issue. We've also told Hibernate that we'd like it to eagerly fetch the Issue when returning the Comment. In normal Java web development we could rely on a plugin to keep the session open for us to lazily load the Issue object only when it's referred to at runtime, but since our Flex application runs external to the JVM we cannot take advantage of this luxury. Now that we have our model objects built we can now create a set of Data Access Objects or “DAOs”. We will need a DAO for Issue and Comment.
2.4 Build the DAOs AppFuse provides some generic implementations for your DAO objects that you can leverage if your DAOs do nothing more than the basic create, retrieve, update, delete (CRUD) operations. Since our IssueDao will do nothing more than these basic operations, there is no need for us to define a concrete IssueDao and can instead use the GenericHibernateDao which you'll see when we wire up the beans in the application context later.
Our
CommentDao however needs to implement a couple of operations that go beyond the basic CRUD operations so we'll first create an interface for the CommentDao shown in listing 2.5.
Listing 2.5 The CommentDao.java ... public interface CommentDao extends GenericDao { List getCommentsByIssueId(Long issueId); void deleteAllCommentsForIssueId(Long issueId); }
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
28
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
The CommentDao has two simple methods, one that returns a list of Comment objects by passing in the issueId argument, and another to delete all of the comments for an issue. The second method is there to facilitate the deleting of issues. Since we have a foreign key relationship from Comment to Issue, we cannot delete an Issue if any Comments are referring to it.
NOTE We defined the relationship from Comment to Issue by annotating the field with a @OneToOne annotation, and could have also defined the reverse of that relationship in the Issue class by including a Set of Comments belonging to an issue. However because of the fact that we could not lazy load those objects, it would have forced us to eager load the Comments into the Set, which would force those Comments to eager load their Issues, which forces the Issues to eager load their Comments, and so on. This usually will occur in a stack overflow because you've effectively got a circular reference that will cause an infinite loop of eager fetching.
Now let's implement the CommentDaoImpl as seen in listing 2.6.
Listing 2.6 The CommentDaoImpl.java ... public class CommentDaoImpl extends GenericDaoHibernate implements CommentDao { public CommentDaoImpl() { super(Comment.class); } @Override @SuppressWarnings("unchecked") public List getCommentsByIssueId(Long issueId) { return getHibernateTemplate().find("from Comment where issue_id = ?", issueId); } } Much like the IssueDaoImpl, CommentDaoImpl extends GenericDaoHibernate but implements CommentDao. The only thing really interesting happening here is that we have a method that returns a list of Comment objects by leveraging the Hibernate template and a query. Spring and Hibernate are a wonderful combination and make for clean and intuitive DAO objects. Now that we constructed the DAOs we can now move on to building some services.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
29
2.5 Build the services Now we just need to expose some services to the web tier. We will be able to take advantage of these services for the Flex client we will be building, starting in chapter 2. Again we'll start with some interfaces like IssueManager in listing 2.7.
Listing 2.7 The IssueManager.java package org.foj.service; import org.foj.model.Issue; import javax.jws.WebService; @WebService public interface IssueManager {
#1
java.util.List getAll(); Issue get(Long id); Issue save(Issue issue); void remove(Long id);
#2 #3 #4 #5
}
Cueballs in code and text #1 IssueManager interface declaration with the WebService annotation #2 Return all Issues #3 Get specific Issue by it's ID #4 Save an Issue #5 Delete an Issue
We define the IssueManager as a WebService by annotating it using the @WebService annotation (#1). IssueManager contains methods defining our basic CRUD operations for reading (#2 and #3), creating and updating(#4), and deleting (#5). Now let's take a look at the CommentManager in listing 2.8.
Listing 2.8 The CommentManager.java package org.foj.service; import org.foj.model.Comment; import javax.jws.WebService; import java.util.List; @WebService public interface CommentManager
#1 {
List findCommentsByIssueId(Long issueId); void deleteAllCommentsForIssueId(Long issueId); Comment get(Long id); #4 Comment save(Comment comment); #5 void remove(Long id); #6
#2 #3
} ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
30
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Cueballs in code and text #1 CommentManager interface declaration with the WebService annotation #2 get comments for an issue id #3 delete all comments for an issue #4 get a comment by its ID #5 save a comment #6 remove a comment
CommentManager is also a WebService (#1) by virtue of it being annotated with the @WebSerivce annotation just as with the IssueManager. It contains a method to return a list of Comment objects by providing an issueId (#2), a method for deleting all comments for an issue id (#3), a method for saving a comment (#4) and lastly a method for deleting a comment
(#6).
Now
let's
provide
some
implementation
for
the
services
like
IssueManagerImpl in listing 2.9.
Listing 2.9 The IssueManagerImpl.java package org.foj.service.impl; import import import import import import
org.appfuse.dao.GenericDao; org.foj.model.Issue; org.foj.service.IssueManager; org.foj.service.CommentManager; java.util.List; javax.jws.WebService;
@WebService(serviceName = "IssueService", endpointInterface = "org.foj.service.IssueManager") public class IssueManagerImpl implements IssueManager {
#1
private GenericDao issueDao; private CommentManager commentManager; public IssueManagerImpl() { }
#2
public IssueManagerImpl(GenericDao issueDao, CommentManager commentManager) { this.issueDao = issueDao; this.commentManager = commentManager; } public List getAll() { return issueDao.getAll(); } public Issue get(Long id) { return issueDao.get(id); } public Issue save(Issue issue) { return issueDao.save(issue); }
#3
#4
#5
#6
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
public void remove(Long id) { commentManager.deleteAllCommentsForIssueId(id); issueDao.remove(id); }
31
#7
}
Cueballs in code and text #1 IssueManagerImpl declaration with the WebService annotation #2 default no arg constructor #3 Constructor that sets the injected IssueDao instance and CommentManager to use #4 Method that returns all Issues #5 get specific issue #6 Save issue #7 remove an issue
The IssueManagerImpl also uses the WebService annotation just as in the interface, but also provides the serviceName and endpointInterface attributes (#1). We provide a default no args constructor (#2) as well as one that will be used by Spring to inject the IssueDao and CommentManager(#3). Next we impement the methods for returning the list of Issues (#4), returning a specific issue (#5), and saving an issue (#6) by delegating the calls to those methods to the IssueDao. The implementation for removing an issue first deletes any comments for the issue by calling the CommentManager and then removes the issue by calling the remove method on the IssueDao. Now let's look at the CommentManager in listing 2.10.
Listing 2.10 The CommentManagerImpl.java package org.foj.service.impl; import import import import import
org.foj.dao.CommentDao; org.foj.model.Comment; org.foj.service.CommentManager; java.util.List; javax.jws.WebService;
@WebService(serviceName = "CommentService", endpointInterface = "org.foj.service.CommentManager") public class CommentManagerImpl implements CommentManager {
#1
private CommentDao commentDao; public CommentManagerImpl() { } public CommentManagerImpl(CommentDao commentDao) { this.commentDao = commentDao; } public List findCommentsByIssueId(Long issueId) { return commentDao.getCommentsByIssueId(issueId);
#2
#3
#4
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
32
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
} public void deleteAllCommentsForIssueId(Long issueId) { commentDao.deleteAllCommentsForIssueId(issueId); } public Comment get(Long id) { return commentDao.get(id); }
#6
public Comment save(Comment comment) { return commentDao.save(comment); } public void remove(Long id) { commentDao.remove(id); }
#5
#7
#8
}
Cueballs in code and text #1 CommentManagerImpl declaration with the WebService annotation #2 Default no args constructor #3 Constructor that sets the injected CommentDao instance to use #4 find all comments for an issue #5 delete all comments for an issue #6 get specific comment #7 save a comment #8 delete a comment
Just like the IssueManagerImpl, the CommentManagerImpl defines itself to be a WebService using the @WebService annotation, and defining its endpoint interface and service name (#1). Next we define a default no args constructor (#2) as well as one that will be used by Spring to inject our CommentDao (#3). We implement the methods to get the comments for an issue (#4), deleting all the comments for an issue (#5), getting a specific comment (#6), saving a comment (#7) and deleting a comment (#8) by delegating to the CommentDAO.
NOTE AppFuse provides GenericManager implementation base classes just as it does for DAOs, but we chose not to use them here because certain WebService consumers such as Flex have difficulty dealing with Web Services that return objects as ArrayOfAnyType, which is what AppFuse will return if we leverage the GenericManagers.
So to work around this
issue we'll be defining and implementing our CRUD operations for the Web Services explicitly.
We are now officially done with the server-side objects and can now wire things together with the Spring configuration and work on the web tier components. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
33
2.6 Wiring things together with Spring Spring provides developers with a means to easily wire objects together to keep application components loosely coupled and testable. Notice how we've wired the model, DAO, and service objects together in listing 2.11. The applicationContext.xml is located in the src\main\webapp\WEB-INF directory, along with some other configuration files.
Listing 2.11 The applicationContext.xml ... #1 #2 #3
#4
...
Cueballs in code and text #1 issueDao #2 commentDao #3 issueManager #4 commentManager
The first bean we define is our GenericDao for the issueDao (#1).
The commentDao
(#2) is defined with our concrete implementation. Next, we create Spring beans for issueManager (#3) and commentManager (#4). The constructor-arg element is used to inject the dependencies into the service class constructor. Now that we've wired things up with Spring let’s construct the web tier starting with some Struts framework action classes.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
34
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
2.7 Construct the web tier In a struts application we are able to utilize the Model-View-Controller design pattern. The pattern encourages separation between the data model, view elements, and controllers that sit between them. The MVC pattern has been a widely adopted in the Java community for many years now and has even made its way into other languages and frameworks, like Flex.
2.7.1 Build the Struts action classes We'll start by building some controller or “action” classes first like IssueAction in listing 2.12.
Listing 2.12 The IssueAction.java package org.foj.action; import org.appfuse.webapp.action.BaseAction; ... public class IssueAction extends BaseAction { private private private private private private
#1
IssueManager issueManager; CommentManager commentManager; List issues; List comments; Issue issue; Long id;
public void setIssueManager(IssueManager issueManager) { this.issueManager = issueManager; }
#2
public void setCommentManager(CommentManager commentManager) { this.commentManager = commentManager; }
#2
... public String list() { issues = issueManager.getAll(); return SUCCESS; }
#3
public String delete() { issueManager.remove(issue.getId()); saveMessage(getText("issue.deleted"));
#4
return SUCCESS; } public String edit() { if (id != null) { issue = issueManager.get(id); } else { issue = new Issue(); }
#5
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
35
comments = commentManager.findCommentsByIssueId(issue.getId()); return SUCCESS; } public String save() throws Exception { if (cancel != null) { return CANCEL; }
#6
if (delete != null) { return delete(); } boolean isNew = (issue.getId() == null); issue = issueManager.save(issue); String key = isNew ? "issue.added" : "issue.updated"; saveMessage(getText(key)); if (!isNew) { return INPUT; } else { return SUCCESS; } } }
Cueballs in code and text #1 IssueAction extends Appfuse BaseAction #2 Setters for IssueManager and CommentManager #3 Returns a list of Issue objects #4 Deletes an issue #5 Edits by issueId #6 Saves an Issue
IssueAction extends the Appfuse BaseAction (#1) that contains many common methods actions rely on. IssueAction has setters for the service objects (#2). These setters will be called by Spring and their instances will be injected into the action class during run time. The IssueAction facilitates controlling communications to the server-side from the web tier. It contains the methods for the view pages to do things like delete (#4), edit (#5) and, most importantly, save issues (#6). The same type of thing needs to happen for comments and can be found in the CommentAction but this time it's all about comments and not issues. All the methods here are facilitating CRUD for the Comment POJO by calling the commentManager service. The CommentAction class can be downloaded from the website if needed. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
36
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Now that we've got our actions in place let's work on some JSP files to actually have a simple UI for managing issues.
2.7.2 Edit the Issue menu item First things first. We have to modify the menu.jsp in order to get to the Issues list. So, let's start with the menu.jsp in listing 2.13.
Listing 2.13 The menu.jsp ... #1 ...
Cueballs in code and text #1 Adding the IssueMenu item to the JSP view file
The menu JSP file reads in the menu xml data. All we need to do to add the Issue menu item
is
add
a
single
line(#1)
to
this
file
that
is
located
in
the
../flex-bugs-
web/src/main/webapp/common directory. In listing 2.14 we will actually provide the xml data for that menu item.
Listing 2.14 The menu-config.xml ... ...
Cueballs in code and text #1 Add the Issue menu item to the menu data xml file
Just adding to the already existing Appfuse plumbing for creating menu items (#1) quickly gives us access to our new features. Now let's create the IssueList.jsp that will be displayed when you click the Issues menu item.
2.7.3 Add some JSP resources The IssueList.jsp, in listing 2.15, will display a list of issues and allow you to add new or modify existing issues from there. The Issue and Comment JSP files will reside in the ../src/main/webapp/WEB-INF/pages directory. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
37
Listing 2.15 The issueList.jsp #1 #2 #3 #4 #6 highlightTableRows("issueList");
Cueballs in code and text #1 Essential tag libraries bundle #2 Variable holds button data #3 Prints out button data for display #4 Variable represents issues list #5 Displays nicely formatted table
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
38
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#6 JavaScript highlights the table rows
To make life easier, we include a JSP that in turn includes a bundle of tag libraries (#1) that are useful for our web application. We have some button data that will be stored in a variable (#2) and a Java Standard Tag Library (JSTL) tag (#3) that will print out the buttons. We create a variable that will hold a list of issues (#4) from the request scope an HTML table that is formatted using the included display tag library (#5). Finally, there's a little JavaScript used to highlight rows of data for us (#6). Now let's have a look at the issueForm.jsp in listing 2.16.
Listing 2.16 The issueForm.jsp #1 #2
#3
#4 ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
39
highlightTableRows("commentList"); #5 Form.focusFirstElement($("issueForm"));
Cueballs in code and text #1 “s” Struts form tag in action #2 Form text input fields #3 CRUD Button bar #4 Comments struts form #5 JavaScript assigns focus
Obviously, the issueForm.jsp will allow a user to add or edit an issue. If you peak into the included ../src/main/webapp/common/taglibs.jsp you will notice that the Struts tag libraries are included and the letter “s” was used for the tag prefix (#1). There are Struts textField elements (#2) that will map to an Issue object. The button bar created will contain save, delete, and cancel buttons (#3). The delete will only display if the issue has an id or already exists. Now let's keep moving and build the commentForm.jsp like in listing 2.17.
Listing 2.17 The commentForm.jsp ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
40
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
As the name entails, the commentForm.jsp provides a Struts form for updating new or existing
comments.
When
submitted,
the
form
will
call
the
comment
manager's
saveComment method. Now that we have the JSP files in place we will need to actually add those properties so that they have real values.
2.7.4 Add some property resources In order for the application's messages to be localized we've leveraged the Java resource bundle
framework.
Add
the
ApplicationResources.properties
properties file
located
like
in in
listing the
2.18
to
the
../flex-bugs-
web/src/main/resources directory. Listing 2.18 The ApplicationResources.properties # -- menu/link messages -menu.issue=Issues menu.viewIssues=View Issues # -- issue form -issue.id=Id issue.project=Project issue.description=Description issue.added=Issue has been added successfully. issue.updated=Issue has been updated successfully. issue.deleted=Issue has been deleted successfully. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
41
# -- issue list page -issueList.title=Issue List issueList.heading=Issues # -- issue detail page -issueDetail.title=Issue Detail issueDetail.heading=Issue Information # -- comment form -comment.id=Id comment.author=Author comment.issueId=Issue Id comment.createdDate=Created Date comment.commentText=Details comment.added=Comment has been added successfully. comment.updated=Comment has been updated successfully. comment.deleted=Comment has been deleted successfully. # -- issue list page -commentList.title=Comment List commentList.heading=Comments # -- issue detail page -commentDetail.title=Comment Detail commentDetail.heading=Comment Information If more langauage support is needed add the same properties with the respective translation to the appropriate properties file in the same directory. Now let's wire up the view components with Struts.
2.7.5 Configure the struts.xml In order to wire up the JSP view components to the controller objects we created we can use the struts.xml located in the ../src/main/resources directory. Listing 2.19 demonstrates the wiring we need for the Issues management.
Listing 2.19 The struts.xml ... #1 /WEB-INF/pages/issueList.jsp #2 /WEB-INF/pages/issueForm.jsp /WEB-INF/pages/issueList.jsp #3 ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
42
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
/WEB-INF/pages/issueForm.jsp issues issues editIssue ${issue.id} /WEB-INF/pages/commentList.jsp /WEB-INF/pages/commentForm.jsp /WEB-INF/pages/commentList.jsp /WEB-INF/pages/commentForm.jsp editIssue ${issue.id} editIssue ${issue.id} editIssue ${issue.id}
Cueballs in code and text #1 issues action loads issueList.jsp #2 editIssue loads issueForm.jsp #3 saveIssue loads issueForm
Struts makes it really simple to wire up the view components quickly and make changes. As you can see, the issue action (#1) will load the issueList.jsp whenever the list() method is invoked on the IssueAction. In the same way, editIssue (#2) will load the issueForm.jsp when the edit() method is called and if that doesn't work for some reason it will go back to the list page. Finally, the saveIssue action will persist an Issue by taking the input from the issueForm.jsp. The remainder of the IssueAction is more of the same but pertains to issue comments.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
43
2.7.6 Configure Hibernate The final thing to do is configure our POJOs with the Hibernate session factory like in listing 2.20. That way when the app is loaded into memory Hibernate knows about these objects. We do this through the hibernate.cng.xml located in the ../src/main/resources directory.
Listing 2.20 The hibernate.cfg.xml
#1 #1
Cueballs in code and text #1 Adding Issue and Comment to the hibernate configuration for loading with the Hibernate sessionfactory
Another simple configuration to take care of. This time we create a class mapping (#1) for each of our model objects, Issue and Comment. If you were to browse to rebuild the the application with the mvn jetty:run-war command the issues button should be available as seen in figure 2.7.
Figure 2.7 The issues list page with the integrated Issues menu button
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
44
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
2.8 Summary In this chapter we set up a Java web application using the Appfuse framework. Appfuse simplified the plumbing involved in building a typical Java web application using many of the popular frameworks out there including Struts, Spring, and Hibernate. In the next chapter we'll get started with building a Flex client that will consume the Flexbugs web services and also call directly with BlazeDS.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
45
3 Getting Rich with Flex
In this chapter
Create a Flex project using archetype
Create a Flex front-end for the sample application
In the last chapter we introduced you to AppFuse and created our sample issue tracking application.
Now it’s time to begin the task of creating the Flex front-end for our sample
application. We’ll start off by incrementally building up the view layer introducing you to a few of the pertinent concepts of Flex as we go.
This chapter is not meant to be a
comprehensive guide to the Flex framework by any stretch of the imagination; however you should still be able to follow along without too much trouble. If you want a more in depth look at the Flex framework check out the title Flex in Action.
3.1 Generate the application structure The first thing we need to do before we get started is to create our Flex application. Since we’ll be using the Flex Mojos Maven plugin we’ll be creating the application in a similar manner as we did for the AppFuse portion of the application.
We’ve taken the liberty to
create a Maven archetype to minimize the amount of manual work we would need to do in order to create the project structure.
FNA (FNA is Not AppFuse) Some folks at Adobe Consulting have started a new project up at Google Code called FNA or FNA is Not AppFuse (http://code.google.com/p/fna/).
The FNA project has similar
goals to that of AppFuse in that they are making an attempt at creating a framework that enables developers a way to jumpstart their RIA applications with Flex and Java.
We
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
46
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
have decided against using this framework for this book, but feel like this project does have potential and warrants a look at if you are starting a new project.
So let’s get started shall we.
Open up a command prompt and navigate to the root
directory of our application. Enter the following command to create our Flex application. $ mvn archetype-create –DarchetypeGroupId=org.foj \ -DarchetypeArtifactId=flex-mojos-arhcetype \ -DarchetypeVersion=1.0-SNAPSHOT \ -DgroupId=org.foj \ -DartifactId=flex-bugs-ria \ -DremoteRepositories=http://flexonjava.googlecode.com/svn/repository This will create a Flex project that slightly resembles a standard Maven project. Since this is not a Java project, there is a slight variation on the project structure. The sources for our Flex application will go in the src/main/flex folder and the tests in src/test/flex folder. Maven will also modify our main project pom.xml and add this project as a module as shown in Listing 3.1.
Listing 3.1 Parent pom.xml ... flex-bugs-ria flex-bugs-web ... Now that our project has been created we need to configure the Flex Mojos plugins for both the Flex project and the AppFuse project.
3.2 Configuration for the flex-bugs-ria module For this application we chose to use the Flex Mojos plugin to leverage the powerful dependency management facilities of Maven as well as avoiding having to write yet another Ant build script.
Listing 3.2 The pom.xml for our Flex application flex-bugs org.foj 1.0-SNAPSHOT 4.0.0 org.foj flex-bugs-ria
#1 #1 #1 #1 #1 #2 #3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
swf 1.0-SNAPSHOT
47
#4 #5
3.2.0 src/main/flex src/test/flex
#6 #6 #6
#7 #7
flex-bugs-ria #8 #9 #9 org.sonatype.flexmojos #9 flexmojos-maven-plugin #9 ${flexmojos.version} #9 true 10.0.0 en_US com.adobe.flex compiler 4.0.0.7219 pom #9 #9 #9 #10 #10 flexmojos-repository #10 http://repository.sonatype.org/content/groups/public/ #10 #10 #10 #10 #10 flexmojos-repository #10 http://repository.sonatype.org/content/groups/public/ #10 #10 #10 #11 com.adobe.flex.framework flex-framework 4.0.0.7219 pom com.adobe.flex.framework ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
48
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
playerglobal 4.0.0.7219 10 swc org.sonatype.flexmojos flexmojos-unittest-support ${flexmojos.version} swc test
#11
1 Define the parent pom 2 This module's groupId 3 This module's artifactId 4 Packaging type 5 This module's version 6 The Flex-Mojos version 7 Specify the source and test source directories 8 The final name for our artifact 9 The flex-mojos-plugin 10 The Flex Mojos repository at Sonatype 11 The Flex and unit testing dependencies
Listing 3.2 shows the resulting pom.xml for our Flex application after we generate the project using the mvn archetype:create command shown in section 3.1. Since this is a part of a multi-module project, the pom.xml lists the parent module's pom as being the parent for this project (#1). Next you'll see the values that you specified for the groupId (#2) and artifactId (#3) defined, as well as the packaging type (#4) of swf since this project is our Flex application and will be compiled to an swf file.
NOTE: FILE ASSOCIATION You may need to associate the SWF file with the Standalone Flash Player or else your build may time out trying to run the flexunit tests. Because flexunit requires the Flash runtime in order to run its test suites, Maven will try to execute the resulting SWF file for your test suite using the default application for SWF files.
Next we set the project's version (#5), which is set to 1.0-SNAPSHOT. The archetype also defines a common property (#6) for the Flex Mojos version so that we can be sure that the plugin (#9) and any dependencies (#11) defined for the Flex Mojos are using the same version. We're also overriding the version for the Flex compiler here to compile it with the Flex 4 compiler and target version of Flash Player.
Since this is not your typical Maven
project, the pom.xml defines the source and test-source directory locations (#7). The pom.xml also defines the repository and plugin repository locations (#10) for the Flex Mojos plugins and dependencies since they don't exist in the central Maven repository.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
49
3.3 Configuring Maven for the flex-bugs-web module Now that we've taken care of configuring Maven to build our Flex application, we need to make some minor modifications to the pom.xml for the flex-bugs-web module in order to get our Flex application to be copied over to the appropriate place in our web application. In order to accomplish this task we'll make use of the maven-dependency-plugin.
Listing 3.3 Configuring the maven-dependency-plugin ... maven-dependency-plugin unpack-config unpack-dependencies generate-resources ${project.build.directory}/${project.build.finalName}/WEB-INF/flex ${project.groupId} resources true jar,swf copy-swf process-classes copy-dependencies true ${project.build.directory}/${project.build.finalName} #5 swf
#1
#2 #2 #2 #2 #2
#3 #3 #3 #3 #3 #3 #3 #4 #4 #4 #4 #4 #4 #5 #5 #5 #5 #5 #5 #5
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
50
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
... 1. The maven-dependency-plugin 2. The unpack-config execution 3. The configuration for the unpack-config execution 4. The copy-swf execution 5. The configuration for the copy-swf execution
Listing 3.3 shows the plugin configuration (#1) that is needed in order for our web application to properly resolve the dependency for our Flex application and ensure that the SWF file gets placed in the proper place. First we define an execution that we'll call "unpack-config" (#2) and tell Maven to execute this during the "generate-resources" phase of the build, and call the "unpack-dependencies" goal on this plugin. Then in the configuration (#3) we tell the plugin to limit the scope of what gets affected by this execution to artifacts with the same groupId as our project and only artifacts of type resources. This will get utilized later as we create a common project for all of the configuration files for BlazeDS that need to be shared between both the web application and the Flex application.
The Maven build lifecycle Maven follows the Convention over Configuration paradigm in many aspects, and the build lifecycle is one of them.
The folks who designed Maven realized that there are
many common steps in every build and developed the concept of the build lifecycle around it. The default lifecycle flows through the following build phases (in order). validate compile test package integration-test verify install deploy For more information on Maven and the build lifecycle you can read the Maven Definitive Guide,
a
free
e-book
written
by
Eric
Redmond
available
at
http://www.sonatype.com/books/maven-book/reference/.
The second execution that we define (#4) is named "copy-swf", and it will do exactly that.
We configure this execution to run during the "process-classes" phase of the build
lifecycle and execute the "copy-dependencies" goal to copy the SWF file from our Flex ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
51
project into the target folder in order to be placed in the proper location before Maven creates the final WAR file (#5). Next let's take a look at how to create an HTML wrapper, or in this case a JSP wrapper for our Flex application.
3.4 Adding a wrapper for our SWF Adobe provides many different templates for you to follow for creating HTML wrapper files for your SWF, located in the /templates directory of your Flex SDK installation. They include everything from the very basic no frills wrapper, to wrappers that include functionality to detect whether or not the client has the correct version of the Flash Player installed and whether or not to support deep linking and history for your application.
NOTE Normally the HTML wrapper would be a HTML file in your web application, however since the Sitemesh filter in AppFuse is configured to decorate anything with a .html extension, we decided the easiest way to circumvent this filter was to make the wrapper a JSP file.
For this application copy the contents of the client-side-detection-with-history folder including the index.template.html, AC_OETags.js file and the history folder from the /templates directory of your Flex SDK installation to the src/main/webapp directory of the flex-bugs-web project. Rename the index.template.html file to flexbugs.jsp, and replace the placeholders in the file with the values shown in Listing 3.4.
Listing 3.4 HTML Wrapper values ${title} -> FlexBugs ${version_major} -> 9 ${version_minor} -> 0 ${reqiured_revision} -> 28 ${width} -> 100% ${height} -> 100% ${application} -> flex-bugs-ria ${bgcolor} -> #869ca7 ${swf} -> flex-bugs-ria.swf We aren't going to go into detail about what is contained in the flexbugs.jsp since it's likely you won't have to change anything inside of it in the future. You can learn more about the various HTML templates that Flex provides in the LiveDocs at Adobe's website at http://livedocs.adobe.com/flex/3/html/wrapper_04.html#178239.
3.5 “Hello World” in Flex Now that we have the web application configured to properly resolve our Flex application dependency, place the resulting SWF file in the proper place and have our HTML wrapper configured, let's write a quick "Hello World!" application in Flex just to verify that everything is working as expected.
In the src/main/flex folder of the flex-bugs-ria project
create a Main.mxml file for our Flex application. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
52
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Listing 3.5 Hello World! in Flex
#1
#2
#3
1. The Flex Application root component 2. Setting the layout 3. A SimpleText component
Listing 3.5 shows a very minimal "Hello World!" application in Flex. It only consists of the root Application component (#1), a layout definition (#2) and a single SimpleText component (#3) which has its text property set to "Hello World!"
NOTE: MAVEN AND HEAP SPACE You may run into heap space problems when compiling your Flex application with the Flex Mojos. If your build fails with a message similar to the following:
[INFO] -------------------------------------------------------[ERROR] FATAL ERROR [INFO] -------------------------------------------------------[INFO] Java heap space [INFO] -------------------------------------------------------[INFO] Trace java.lang.OutOfMemoryError: Java heap space In order to fix this issue all you need to do is define an environment variable named
MAVEN_OPTS and set its value to "-Xmx512m" adjusting the memory size as needed. To build our Flex application you first need to run mvn install from the flex-bugsria directory. That's it. This will build the flex-bugs-ria project, and then deploy the resulting artifact into your local Maven repository so that the flex-bugs-web project can include it as a dependency in its pom.xml. Once it is finished building you can navigate to the flex-bugs-web directory and run the application by typing mvn jetty:run-war on the command line.
This will start up a Jetty instance and deploy your war inside this
instance so that you can visually verify everything is working.
Once you see the output
shown in Listing 3.6 your application is now running. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
53
Listing 3.6 Console output from the maven-jetty-plugin ... 2009-03-28 19:31:14.206::INFO: Started
[email protected]:8080 [INFO] Started Jetty Server [INFO] Starting scanner at interval of 3 seconds. Open up your favorite browser and navigate to http://localhost:8080/flexbugs.jsp and you should see a screen similar to Figure 3.1.
Figure 3.1 Our "Hello World!" application
Not terribly exciting, however it's nice sanity check to see that our application is configured correctly. Now that we have the obligatory "Hello World" application out of the way, let's get on with the task of developing the real application.
3.6 Developing the FlexBugs application In order to begin developing our application, we should at least have some sort of idea of what we would like it to look like. Figure 3.2 shows a mockup of what our application should look like. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
54
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 3.2 - A mockup of the Flex Bugs application
The application is broken up into three main areas of concern in a sort of modified Master/Detail view with a second detail view for any comments that exist for the selected issue. Our application can also be broken up into a header, footer and main application area. Defining our application in these terms achieves a couple of different objectives. First, by separating our application into these different pieces, we can create separate MXML files to help keep our code manageable. Another benefit is that by breaking up our application into separate MXML files, we have the ability to reuse parts of our application if the need arises. Next we're going to disseminate our application into manageable chunks and we'll start by introducing some of the container and navigation components we'll be using to build this application starting with the ViewStack navigation component.
3.6.1 Introducing the ViewStack The ViewStack component is a navigation component that allows you to stack up a collection of views and selectively display them.
Unlike traditional web applications, Flex
applications typically don't have many pages that make up the application.
A Flex
application typically has one application that will change its view state depending on which part of the application is active. The ViewStack is one of the Flex components that allows you to do this by bringing the active view to the foreground and hiding the inactive views in the background as shown in Figure 3.3. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
55
Figure 3.3 How the ViewStack works
You can control the ViewStack in a number of ways; the most common is to utilize one of the navigation components such as the LinkBar, ButtonBar, or ToggleButtonBar. For the FlexBugs application we'll leverage the ToggleButtonBar to facilitate switching the view state.
In the top right corner of our mockup you'll see two buttons labeled "Details
View" and Graph View", these are the two views that we'll make use of in this application. We'll develop the "Details View" in this chapter and come back to develop the "Graph View" in Chapter 10 when we talk more about graphing components.
Listing 3.7 Defining the ViewStacks
#1
#2 #2 #2
#3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
56
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#3 #3
1. Define our view stack 2. Add the first view component 3. Add the second view component
Listing 3.7 shows our Main.mxml after we add the ViewStack components to our application. After we remove our HelloWorld code, create a ViewStack element (#1) and give it an id of "mainViewStack", this will become important later as we define our
ToggleButtonBar, as the dataProvider attribute for the ToggleButtonBar will be set to this ViewStack component. We want this ViewStack to use up all available horizontal and vertical space so we set its width and height to 100%. Next we add two Canvas components (#2 and #3) to our ViewStack giving them ids of "view1" and "view2" respectively. These two Canvas components will be the two main views our ToggleButtonBar will control. The label attribute of these two components will be what gets displayed as the text of the two ToggleButton controls so we set those to "Details View" and "Graph View", respectively. Inside of these two Canvas containers we simply put some Text components as placeholders, so that we can see how the demonstrate how the ToggleButtonBar will control our two view states in the next section.
3.6.2 HeaderView Before we go much further with building up the application we need to create our header view so that we can control our view states that we just created. In the src/main/flex folder create the directory structure shown in Figure 3.4 to house our view components.
Figure 3.4 Folder structure for view components
ActionScript and Flex follow a similar packaging structure as Java so we will leverage that aspect in order to keep our source files organized similar to how we would do in a Java project. Inside of the view folder create a new file called Header.mxml, where we will put the code for the header for our application.
Listing 3.8 Header.mxml
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
#2
57
#4 #5
#5 #4 #4 #6 #4
1. Component extends Group 2. Define the layout 3. Import and declare a ViewStack member variable 4. Spacers for layout 5. Text field for our Title 6. ToggleButtonBar for controlling ViewStack
Listing 3.8 shows the code for our Header.mxml component. There's really not a whole lot to it. The component itself extends the Group component (#1), and defines its layout (#2) as being HorizontalLayout, meaning that all of the components inside of it will be laid out horizontally as opposed to vertically or absolutely. Next we define a public member variable (#3), which will be used to allow our main application to pass in the ViewStack that the ToggleButtonBar (#6) will control. In order to do that we create a Script block and enclosing some ActionScript inside of a CDATA section, so that any characters that may potentially be parsed as XML are handled correctly. For the Application Title we have a SimpleText component (#5) with a couple of attributes defined on it. The first one of these attributes is the text attribute, which simply sets the text to be displayed in our application. Flex has support for CSS styles similar to what you may be used to working with in web applications, and we'll make use of this in Chapter XX to change the appearance of our application.
Finally there are a few Spacer
elements (#4), which we use to make sure everything is laid out properly.
The Spacer
elements do just what you would expect, they simply take up space and are used to fill in blank space so that you can effectively lay out components in your application. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
58
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Listing 3.9 Adding the Header to Main.mxml
#1
#2
1. added namespace for the view components 2. added header to application
Listing 3.9 now shows our updated Main.mxml file that now includes our Header.mxml component we just created. First we defined a custom namespace for our view components by adding the code shown at (#1). Next we add our custom component to the Main.mxml by using this custom namespace prefix and pass in a reference to the ViewStack component by using the binding expression "{mainViewStack}" (#2).
Figure 3.5 The Header view added.
Now you should be able to build and run the application as outlined earlier, and be presented
with
something
that
resembles
Figure
3.5.
When
you
click
on
the
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
59
ToggleButtons in the upper right hand corner, you should see the text in the main part of the application change. Next let's build a very simple footer component for our application.
3.6.3 FooterView Inside the same folder where we just created the Header.mxml in the previous section, create another file named Footer.mxml. Though it may seem like a little bit of overkill to separate our footer into its own MXML file, we'll do it anyway just to get you in the habit of systematically breaking your Flex application into smaller, more manageable pieces.
Listing 3.10 Footer.mxml The code in Listing 3.10 shows our footer file, and is rather unremarkable. Similar to how we did the header earlier, our footer extends the Group component.
It only contains a
single SimpleText component, which contains our copyright information, and sets its
textAlign attribute to "center". Once again we leverage a couple of Spacer elements to assist in layout.
Listing 3.11 Main.mxml updated with footer ... ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
60
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#1
1. Added the footer to the Main.mxml
Next we add the footer to our application much like we did earlier for the header. Near the end of the Main.mxml, place the tag for the footer as shown in Listing 3.11. Next let's move on to creating the view component for our master view.
3.6.4 MasterView Now we start to get to the more interesting parts.
The master view if you'll recall from
Figure 3.2 consists of a few different components. At the top of the master view reside a data grid component, and two buttons at the bottom of the data grid for adding and removing issues.
Listing 3.12 MasterView.mxml #1
#2
#3
1. Extending Panel 2. The DataGrid
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
61
3. Control Bar for Add/Delete buttons
First create a file inside of the org/foj/view folder called MasterView.mxml.
This
will contain the code necessary to create our master view, which is shown in Listing 3.12. This component will be based on the Panel component, which is one of the various layout containers available in the Flex framework. The Panel component provides us with a title bar area where we can provide the group of components contained within a meaningful title much like the tag in HTML, or a group box control, for those more familiar with desktop development, would do. The next component you'll see in the code listing is the
DataGrid (#2). The DataGrid is a powerful component that is used to display tabular data, and is also one of the fundamental controls used in data-driven applications.
The
DataGrid and it's bigger brother the AdvancedDataGrid give you the ability to actually edit rows of data within the table cells, but for our application we're not going to leverage that functionality in favor of using a detail view. Lastly we have a ControlBar (#3) at the bottom of our Panel that will contain the buttons for adding and removing issues from the application.
3.6.5 DetailView The next view we're going to develop is the detail view, where we will allow the users of the application to modify the data fields for the issues that are displayed in the master view. The details view will contain a form containing the various fields that can be updated for the issues in our application. Begin just as we did earlier for the master view, by creating a file named DetailView.mxml in the org/foj/view folder. Listing 3.13 shows the code we'll be adding to that file.
Listing 3.13 DetailView.mxml
#1 #2 #3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
62
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#4
1. 2. 3. 4.
Extends Panel The Form container FormItem components ControlBar
Similar to the master view, the details view component will be based off of the Panel layout container (#1).
Next we add in a Form (#2) container to help organize the input
fields that will be used to ultimately update the issues in the application. Unlike in HTML, the
Form container in the Flex framework serves no other purpose than to group form controls on the page. You do not need to wrap fields in a Form tag in order to submit data to the back end; it's simply there for aesthetics. Inside of the Form we wrap each input field inside of a FormItem (#3) component that provides some styling, a label and layout for each of the form fields. Lastly the detail view contains a ControlBar (#4), which again contains the buttons that control saving the data and cancelling the edits.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
63
3.6.6 CommentsView The final section of the application we'll layout is the comments view which will eventually list any comments that are added to our issues. First create the CommentsView.mxml file in the org/foj/view folder just as we did for all of the other view components. Initially the comments view will only contain some simple placeholder containers, but later on in chapter XX we'll be developing a custom item renderer to display the comments as well as let us add new comments.
Listing 3.14 CommentsView.mxml #1
#2
#3 #4 #4 #4
1. Extending Panel 2. List for the comments. 3. ControlBar for buttons 4. Buttons for operations on comments
Listing 3.14 shows the code for the comments view. Just like we did in the other main pieces of our application, the comments view is based on the Panel component (#1). Next we add a List component (#2), which we'll use to contain a list of our comments. At the bottom of the Panel we add a ControlBar (#3) to hold the 3 Button components (#4) we'll define to operate on the comments.
3.7 Laying out the components Now that we've got all of the components defined, let's add them to the Main.mxml and define our overall application layout. In Flex, all components can be laid out within other containers generally in one of three ways, horizontally, vertically, or absolutely.
In most
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
64
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
cases it will be desirable to go with either horizontal or vertical layouts, especially if you want your application to resize appropriately. In order to achieve the layout you ultimately want you often times will need to nest layout containers within one another as illustrated in Figure 3.6.
Figure 3.6 The layout for our application
The diagram in Figure 3.6 shows the nesting of layout containers that was necessary in order to duplicate what was shown in our mockup shown in Figure 3.1 earlier. Listing 3.15 shows our updated Main.mxml with all of the layout components necessary.
Listing 3.15 Laying out the application ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
65
#1 #2 #3 #4 #5 #6
Just as was shown in the diagram in Figure 3.6, we start out with a Canvas (#1) container to hold all of the nested layout containers for the main ViewStack. Inside of this we create an HDividedBox (#2) setting it's width and height to 100%, meaning that we want it to take up all available space. Within that we place a VDividedBox (#3), which will contain the MasterView (#4) and the CommentsView (#5).
Lastly we add the
DetailView (#6), which will occupy the right hand side of the HDividedBox. Now our application is almost complete. Next let's create a component which we'll use as a modal popup to edit the comments that are in the List view of our CommentsView component.
3.8 Creating a popup component The final component we'll develop in this chapter is a modal popup form that we'll wire into the application in the next chapter for adding new comments and editing existing comments.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
66
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 3.7 Our popup for editing comments
Figure 3.7 is a screenshot showing what our popup will end up looking like. Listing 3.16 shows the code for our popup.
Listing 3.16 EditCommentForm.mxml
#1 #2
#3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
67
1. Extends TitleWindow 2. Add a Form 3. Control Bar
Our popup window is fairly simple. We define our component to extend the TitleWindow component (#1), which is the component you'll typically use for popups.
Next we add a
Form and a bunch of FormItems (#2) just like we did earlier for the DetailView. Finally we add a ControlBar (#3) to the window to hold the Button components for controlling the popup.
3.9 The finished application Well, the application is far from finished, but we're done for now. Now is as good a time as any to build our application and see the progress we've made.
The application won't be
functional until the end of the next chapter, but it's always reassuring to see what the finished product will look like anyway.
Figure 3.8 The finished application ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
68
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
From the root of the flex-bugs-ria project run the command mvn install.
The
build should complete successfully, if not go back through and double-check your code to ensure that it matches the code in the listings. Next inside the root of the flex-bugs-web project run the mvn
jetty:run-war command to start up the embedded Jetty web
container and deploy your war file.
Once the container is up and running navigate to
http://localhost:8080/flexbugs.jsp and you should see something that resembles Figure 3.8.
3.10 Summary This chapter moved quickly and only briefly introduced many of the components available to you in the Flex framework. We started out with an idea of what we wanted our application to look like and decomposed that into several smaller components.
By doing that we not
only made our code more manageable, but as you'll see in the next chapter, it will also make our presentation models and event handling more manageable.
Once we had all of our
pieces built up, we were able to illustrate some of the basics of layout containers in Flex and put our application together. We're hopeful that you were able to follow along, however if you feel like you need more information about layouts and the components of the Flex framework,
a
good
place
to
start
would
be
the
LiveDocs
at
Adobe's
site
(http://livedocs.adobe.com/flex/3/html/help.html). Now that we've built up our Flex application and layed out our components, where do we go from here? In the next chapter we're going to begin connecting our Flex application to the Java back end we developed in Chapter 2 using the WebService component as well as the HTTPService component. Later on in Chapter 6 we'll refactor this to use the BlazeDS framework to talk directly to our Java application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
69
4 Connecting to Web Services
In this chapter
Model View Presenter
Event Dispatching and Handling
Calling a Web Service
Interfaces and Views
In the last chapter we designed the user interface for our Flex application, but it won't be very useful to anyone until it's able to communicate with some sort of server-side component. In this chapter we're going to continue building up our client-side application. Many Flex books attempt to hide any complexity when it comes to dealing with client-side code and ActionScript, usually by having you create all of your WebService components and event handling code in your MXML files.
However as many developers will agree, this
solution does not scale very well and quickly shows its warts in any but the most trivial application. What we've decided to do instead is to architect our client-side code in such a manner that it should not only scale well, but also isolates your view from presentation and also any external services.
This allows our application to be flexible enough to easily refactor and
replace one implementation of external service to another with mimimal code changes, meaning you could change your application from calling web services to leverage BlazeDS painlessly as you'll see in chapter 5. We'll do this by using a well-known design pattern by the name of Model View Presenter (MVP). Since a good portion of the interaction between the Flex framework and the server-side occur as asynchronous calls, we'll go over events and event handling. We'll go over how to utilize events to facilitate communication between separate sections of the application. We'll also learn how to create the WebService component and call the web services we defined in ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
70
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
our application back in chapter 1. By the time we're done with this chapter you should have a functioning application that communicates with our Java server-side.
4.1 Model View Presenter To build the backend for our Flex application, we're going to make use of a popular GUI architectural pattern called Model View Presenter, and more specifically a variation of that pattern called Passive View. As you may have guessed already, the Model View Presenter, or MVP for short, pattern consists of three main components: a Model, a View and, of course, a Presenter. By leveraging the Passive View architecture we are able to create what Michael Feathers
describes
as
a
"Humble
Dialog
(http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf).
Box" The primary
motivation behind following this style of development is that we are able to remove as much logic out of our User Interface as possible
Figure 4.1 The relationship between the Model View and Presenter
Figure 4.1 shows the relationship between the three components of the MVP pattern. The first element of the MVP triad is the Presenter. As the diagram shows, all information flows out of the Presenter to either the Model or View. The Model and View are isolated from each other and should not only have no knowledge of each other, but should also not have any knowledge of the Presenter either.
But what about Data Binding? For those of you who are familiar with Flex already, you'll probably notice sooner or later that we've decided to not use the built-in data binding functionality that Flex provides. Because we're following the Passive View pattern for this application, we're relying on the Presenter to push any changes to the data being displayed to the View. The main reason
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
71
we do this if for testability. It's much easier to unit test the presentation behavior if it is in the Presenter than if it is in the View.
All communication with the Presenter should be done through events. The Presenter is then responsible for maintaining the state of the application, making any necessary calls to the Model and update the View when necessary. You can read more about the MVP pattern and
the
Passive
View
updated
pattern
at
Martin
Fowler's
blog
at
(http://martinfowler.com/eaaDev/PassiveScreen.html).
4.2 WebServices in Flex Web services are a fairly common way to do application integration, especially when there are disparate platforms involved. Because of the availability of XML parsers for most every common programming language in use today, web services are a prime candidate for applications to share information with each other. These days, there are two major styles of web services:
SOAP based web services, which are primarily service oriented
RESTful web services, which are primarily resource oriented.
For this application we'll make use of the WebService component to talk to our SOAP based web wervices we created in Chapter 1.
We won't get into using the HTTPService
component in our example, which can be used to connect to RESTful web services, but it's similar enough to using the WebService component that if you decide to use it you should be able to modify the code without too much effort. Exposing your business functionality via web services also has the least amount of impact on your server side, as it doesn't couple your application to the clients that will be consuming the service. As you'll see a little bit later in chapter 5, when we expose a service for consumption using BlazeDS, the remote service is now coupled to only clients who can communicate using the AMF binary protocol that BlazeDS uses. Web services are a common way to integrate applications especially if they run on different platforms. As it happens, Flex falls into this category since it needs to communicate with the server side, which is most likely not written in ActionScript. With that in mind, the fine folks at Adobe have made integrating your Flex application with your backend using web services a fairly trivial endeavor.
Most Flex books will show you how to call your web
services by creating your service components directly in your MXML files, but since we're striving for reusability, maintainability, and overall clean code, we're going to encapsulate our WebService objects in our Model.
4.3 Dispatching and handling events Dispatching and handling events is very fundamental to doing Flex development, and also to doing development using the Passive View pattern described above, since all interaction with the Presenter is done through dispatching events. In this section we'll discuss how to enable communication between the various parts of the application via events. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
72
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
4.3.1 Creating a custom event Many components in Flex have their own built-in events, such as the click event on a button. However if you want to send any data along with your event, like which row in a DataGrid was selected for example, you'll need to create your own custom event class that extends the flash.events.Event class.
NOTE ActionScript, similar to Java, uses packages to logically group related classes and avoid naming conflicts. To declare a class as belonging to a particular package, you use the package keyword, followed by the package name a set of braces containing the code belonging to that package.
Then you place the class file in a folder structure
corresponding to the package name you defined, for example, if your package name is com.example, then the class must be located in a com/example folder of your source tree.
When trying to differentiate between events, you have a couple of choices.
You can
either create a separate subclass for each event that you wish to react to, or create fewer events and use the "type" attribute to filter out only the events you want to add event listeners for, like what's shown in Listing 4.1.
Listing 4.1 UIEvent.as package org.foj.event { import flash.events.Event; public class UIEvent extends Event { public static var SELECTED_ISSUE_CHANGED:String = "selectedIssueChanged"; public static var REFRESH_ISSUES_BUTTON_CLICKED:String = "refreshButtonClicked"; public static var REMOVE_ISSUE_BUTTON_CLICKED:String = "removeButtonClicked"; public static var SAVE_ISSUE_BUTTON_CLICKED:String = "saveIssue"; public static var SELECTED_ISSUE_SAVED:String = "selectedIssueSaved"; public static var CANCELLED_ISSUE_EDIT:String = "cancelledIssueEdit";
#1 #2
public static var SELECTED_COMMENT_CHANGED:String = "selectedCommentChanged"; public static var ADD_NEW_COMMENT_BUTTON_PRESSED:String = "addNewComment"; public static var EDIT_COMMENT_BUTTON_PRESSED:String = "editComment"; public static var DELETE_COMMENT_BUTTON_PRESSED:String = "deleteComment"; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
73
public static var SAVE_COMMENT_BUTTON_PRESSED:String = "saveComment"; public static var CANCEL_EDIT_COMMENT_BUTTON_PRESSED:String = "cancelEditComment"; public static var COMMENTS_UPDATED:String = "commentsUpdated";
#2
public var data : *;
#3
public function UIEvent(type : String, bubbles : Boolean = true, cancelable : Boolean = false) { super(type, bubbles, cancelable); }
#4
} } 1 Extending Event 2 Constants for event type 3 Variable for sending information with our event 4 Constructor
In Listing 4.1 we've created a custom event class called UIEvent. We've defined a whole bunch of constants for the different types of events that will be dispatched. There is also a field defined called data and will accept any type of variable. payload we wish to pass along with the event.
This will hold any kind of
Lastly we overload the constructor and
specify some default values for whether or not the events should bubble up the chain and be cancelable. Next we'll look at how to dispatch these events.
4.3.2 Event dispatching In order for our application to be able to react to any events, we first have to dispatch them. Since every component in Flex is an ancestor of UIComponent, you have the ability to dispatch an event by simply calling the dispatchEvent method within your MXML, however event bubbling only travels upwards towards a components parent. What we need in order for our application to work as we expect is a mechanism to allow sibling components to intercept events being dispatched to notify us of a button being clicked, or the selected item in
a
DataGrid
being
changed.
In
order
to
accomplish
this
we
will
create
an
EventDispatcherFactory so that all event dispatching and subscribing happens with the same EventDispatcher object.
Listing 4.2 EventDispatcherFactory.as package org.foj.event { import flash.events.EventDispatcher; public class EventDispatcherFactory{ private static var _instance:EventDispatcher;
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
74
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
public static function getEventDispatcher():EventDispatcher { if (_instance == null) { _instance = new EventDispatcher(); } return _instance; }
#2
} } 1. Singleton instance of EventDispatcher 2. Factory method to get the EventDispatcher
Listing 4.2 shows the code for the EventDispatcherFactory. There's not a whole lot to it. It consists of a factory method getEventDispatcher #2 which returns a singleton instance of an EventDispatcher #1. We'll use this instance of EventDispatcher to dispatch and subscribe to events. That way any part of our application can dispatch and react to events regardless of where in the application's hierarchy of components and classes it exists.
Now that we
have our custom events and a way to dispatch them, let's get on to enhancing our application to make it a little more interactive and useful.
4.4 Creating Issue and Comment transfer objects In order to ease handling the responses from calling our web services we need to duplicate our data objects we defined in Chapter 1 in ActionScript so that we can effectively deal with our data on the client side.
Listing 4.3 Issue.as package org.foj.dto { public class Issue { public public public public public public public public public public public
var var var var var var var var var var var
id:int; project:String; description:String; type:String; severity:String; status:String; details:String; reportedBy:String; reportedOn:Date; assignedTo:String; estimatedHours:Number;
#1
#1
public function Issue(issue:* = null) { if (issue != null) {
#2
this.id = issue.id; this.project = issue.project; this.description = issue.description; this.type = issue.type; this.severity = issue.severity; this.status = issue.status; this.reportedBy = issue.reportedBy; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
75
this.reportedOn = issue.reportedOn; this.assignedTo = issue.assignedTo; this.estimatedHours = issue.estimatedHours; this.details = issue.details; } } } } 1 Public member variables 2 Convenience constructor
Listing 4.3 shows the Issue data object.
There's not much to it.
It contains several
public member variables which match the properties that are in its Java counterpart. Then we define a convenience constructor (#2) for constructing an Issue. The reason for this is because our WebService will not return actual Issue objects, but rather construct an ObjectProxy, which is an anonymous dynamic object that contains the same properties as our Issue object.
So we define a constructor that will allow us to create an actual Issue
object from this ObjectProxy.
Listing 4.4 Comment.as package org.foj.dto { public class Comment { public public public public public
var var var var var
id:int; issue:Issue; author:String; createdDate:Date; commentText:String;
public function Comment(comment:* = null) { if (comment != null) { this.id = comment.id; this.author = comment.author; this.createdDate = comment.createdDate; this.commentText = comment.commentText; this.issue = new Issue(comment.issue); } }
#1
#1 #2
} } 1 Public member variables 2 Convenience constructor
Listing 4.4 shows our Comment data object, which looks almost identical to the Issue data object. It contains a few member variables (#1) and a convenience constructor (#2). Next let's get down to the task of enhancing our user interface by applying the Model View Presenter pattern to our application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
76
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
4.5 Enhancing the Master View The first part of our application that we'll be enhancing is the Master View. We'll start out by creating the Presenter for the Master View. Then we'll create the Issues model, which will encapsulate our interaction with the web services we created back in Chapter 1. When we're done with all of that, we'll update our View to dispatch the necessary events in order to function properly.
4.5.1 Creating a Presenter for the Master View Since the Presenter is responsible for most of what goes on in our application, it will be the first part of the MVP triad that we'll be creating. The Presenter for the Master View part of our application only has a couple of different events that it will need to react to. First, since our application is stateful by nature, we need to refresh the list of Issues whenever any change to either the list of issues gets changed, i.e. an issue is created or removed, whenever an individual issue is updated, or when the user clicks on the "Refresh Issues" button. The other type of event it needs to react to is when the user clicks the "Remove Issue" button. Listing 4.5 shows the code for our MasterPresenter class.
Listing 4.5 MasterPresenter package org.foj.presenter { ... public class MasterPresenter { private var _view:MasterViewComponent; private var _issueModel:SoapIssueModel;
#1 #2
public function MasterPresenter(view:MasterViewComponent = null) { #3 this._issueModel = new SoapIssueModel(); this._view = view; EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.REFRESH_ISSUES_BUTTON_CLICKED, getIssues); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.SELECTED_ISSUE_SAVED, getIssues); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.CANCELLED_ISSUE_EDIT, getIssues); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.REMOVE_ISSUE_BUTTON_CLICKED, removeIssue);
#4
#4
#4
#4
} private function getIssues(event:UIEvent = null):void { CursorManager.setBusyCursor(); _issueModel.getIssues(new AsyncResponder(getIssuesResult, handleError)); }
#5
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
77
private function removeIssue(event:UIEvent):void { CursorManager.setBusyCursor(); var selectedIssue:Issue = event.data; _issueModel.removeIssue(selectedIssue.id, new AsyncResponder(removeIssueResult, handleError)); }
#6
private function getIssuesResult(event:ResultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); var issues = new ArrayCollection();
#7
for each(var item:Object in event.result) { var issue:Issue = new Issue(item); issues.addItem(issue); } _view.masterViewDataGrid.dataProvider = issues; } private function removeIssueResult(event:ResultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); _view.masterViewDataGrid.selectedIndex = -1; var itemChangedEvent:UIEvent = new UIEvent(UIEvent.SELECTED_ISSUE_CHANGED); itemChangedEvent.data = new Issue(); EventDispatcherFactory.getEventDispatcher() .dispatchEvent(itemChangedEvent); getIssues(); } private function handleError(event:FaultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); Alert.show(event.message.toString()); }
#8
#9
} } 1 The View 2 The Model 3 Constructor 4 Subscribing to events 5 Getting the issues 6 Removing an issue 7 Responding to the getIssues result 8 Responding to the removeIssue result 9 Responding to fault event
The first thing to not about the MasterPresenter is that it contains references to the other two pieces of the MVP triad, the view (#1) and the model (#2). (#3), which takes as an argument its view.
Next is the constructor
The reason we're doing this is that we'll be
constructing our Presenters in the view components as they're created. The view will then ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
78
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
pass in a reference to itself when creating the Presenter. We then create an instance of the Model we'll be calling and register a few event listeners for the events that our Presenter needs to react to (#4). Next we have methods that get called as a result of the events being fired by our View. The first method getIssues(#5) is to react to events that require our application to refresh our DataGrid.
It first calls the CursorManager to change the mouse cursor to the "busy"
cursor. Then it calls our Model's getIssues method, passing in an AsyncResponder, which is a way to provide callback methods to the Model to call when a result gets returned. When a ResultEvent gets fired by our Model, it will call the getIssuesResult (#7) callback method, which changes our mouse cursor back to the normal cursor and updates our DataGrid by setting its dataProvider property to the collection of Issues returned from the Model. As we stated earlier, our web service returns a collection of ObjectProxy objects that we need to iterate through to create actual Issue objects for our view.
WebServices, AsyncToken and IResponder In Flex, calls to remote services happen asynchronously, and return an AsyncToken object that you can then use to add callbacks to be executed when either a ResultEvent or FaultEvent returns as a result of the remote service call. One way to bind these event handlers to an AsyncToken is to add an object that conforms to the IResponder interface, such as an AsyncResponder, which takes in a callback to call on a ResultEvent and one to call if it returns a FaultEvent instead in its constructor.
There are also methods to handle when the user clicks on the "Remove Issue" button (#6) and its callback for a successful return (#8). Lastly we have a very simple and generic error handling function (#9) which simply returns our cursor back to the normal state, and pops up an Alert box containing the error message that was returned from our remote call. Obviously you'll want to replace this simplistic error handling when developing real applications, but for our simple example, this will suffice.
Now let's create our Model
component.
4.5.2 Creating an Issue Model The next component in our MVP triad that we'll implement is our Model. The Model in MVP is sometimes also referred to the Domain Model, it represents the core of our business domain and is often responsible for manipulating data in some sort of relational database, or in our case calling some external service that will handle persisting our data for us.
Listing 4.6 IssueModel.as package org.foj.model { import mx.rpc.AsyncToken; import mx.rpc.IResponder; import mx.rpc.soap.WebService; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
79
import org.foj.dto.Issue; public class IssueModel { private var _issueService:WebService; public function IssueModel() { _issueService = new WebService(); _issueService.wsdl = "http://localhost:8080/services/IssueService?wsdl"; if (_issueService.canLoadWSDL()) { _issueService.loadWSDL(); } }
#1 #2 #3
public function getIssues(responder:IResponder):void { var token:AsyncToken = _issueService.getAll(); token.addResponder(responder); }
#4
public function removeIssue(id:Number, responder:IResponder):void { var token:AsyncToken = _issueService.remove(id); token.addResponder(responder); }
#5
} } 1 Create new WebService 2 Set its wsdl property 3 Load the wsdl 4 getIssues 5 RemoveIssues
Listing 4.6 shows the code for our IssueModel so far. First, in the constructor, we create a
new
WebService
object
(#1)
and
set
its
wsdl
property
to
http://localhost:8080/services/IssueService?wsdl (#2). Next we need to call the loadWSDL method (#3) on the WebService so that our WebService will download the WSDL and be able to make calls against our web service.
This is a necessary step when defining your
WebService in ActionScript as opposed to using the WebService MXML tag. Lastly we define our two business methods getIssues (#4) and removeIssue (#5), which simply call the web service and add the responder callback we passed in from the Presenter as a responder to the AsyncToken. Let's wrap up this MVP triad now by refactoring our MasterView.
4.5.3 Updating the Master View Now that we've got our Presenter and Model in place, let's update our MasterView to add some functionality to it.
Listing 4.7 MasterView.mxml
#1
#6
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
81
#7
#7
#8
#9
1 creationComplete event 2 Bootstrapping the Presenter 3 refreshList 4 selectedItemChanged 5 removeSelectedIssue 6 datagrid itemClick event 7 DataGridColumn bindings 8 Refresh List click event 9 Remove Selected Issue click event
Listing 4.7 shows our updated MasterView.mxml.
The first change you'll notice is the
addition of a handler for the creationComplete event of our component (#1). We make use of this event to help bootstrap our Presenter (#2) and make a call to the refreshList (#3) method to populate our DataGrid automatically on startup for us, which we use to respond to our click event on the "Refresh List" button as well (#8). Next we define an event handler method called selectedItemChanged (#4) which responds to any clicks within the DataGrid (#6). We also define an event handler method called removeSelectedIssue (#5) to respond to any clicks of the "Remove Selected Issue" button (#9). To ensure that this button is only enabled when we have an actively selected row in the DataGrid, we've bound the enabled property of the button to the selectedItem property of the DataGrid. Lastly we've bound the DataGridColumn items in our DataGrid to the properties we've defined on our Issue data object earlier. This will allow the DataGrid to bind the column values to the objects in its data provider.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
82
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
4.6 Enhancing the Detail View The next MVP triad we will create is for our Detail View.
Just like we did in the previous
section, we'll begin by creating the Presenter, then add the necessary methods to the IssueModel, and finish up by enhancing the View.
4.6.1 Creating a DetailPresenter The DetailPresenter must maintain a little bit more state than the MasterPresenter did. Since the code necessary for the DetailPresenter is a little more complex, we'll break it up into a couple of smaller chunks to process, starting with the code in Listing 4.8.
Listing 4.8 DetailPresenter.as package org.foj.presenter { ... public class DetailPresenter { private var _issue:Issue; private var _view:DetailView; private var _issueModel:IssueModel;
#1 #2 #3
public function DetailPresenter(view:DetailView) { this._issueModel = new SoapIssueModel(); this._view = view; this._issue = new Issue(); view.issueTypes = new ArrayCollection( ["Bug", "Feature Request", "Enhancement"] ); view.severityTypes = new ArrayCollection( ["Minor", "Major", "Severe"] ); view.statusTypes = new ArrayCollection( ["Open", "In Progress", "On Hold", "Finished"] );
#4
#5
EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.SELECTED_ISSUE_CHANGED, changedIssue); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.SAVE_ISSUE_BUTTON_CLICKED, saveIssue); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.CANCELLED_ISSUE_EDIT, cancelChanges);
#5
#6
#6
} ... 1 The selected issue 2 The view
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
83
3 The model 4 Initialize components 5 Populate combo boxes 6 Add event listeners
The DetailPresenter not only holds references to the View (#2) and Model (#3), but it also needs to hold a reference to the currently selected issue (#1) from the DataGrid in the MasterView. In the constructor we initialize our Model, set our View to what is being passed in, as well as creating an empty Issue object (#4).
Next the Presenter sets the possible
values in the combo boxes for our issue types, severity, and status (#5) by creating ArrayCollections with the possible values and setting the dataProvider property of the corresponding ComboBox. Then we add a few event listeners (#6) to respond to the events that our Detail View will fire, as well as the changed issue event that the MasterView fires when the selected item in the DataGrid changes. Listing 4.9 shows the rest of the code for our DetailPresenter.
Listing 4.9 DetailsPresenter continuedx ... private function get selectedIssue():Issue { return this._issue; }
#1
private function set selectedIssue(issue:Issue):void { this._issue = issue; _view.issueId = issue.id == 0 ? "" : issue.id.toString(); _view.project = issue.project; _view.description = issue.description; _view.type = issue.type; _view.severity = issue.severity; _view.status = issue.status; _view.details = issue.details; _view.reportedBy = issue.reportedBy; _view.reportedOn = issue.reportedOn; _view.assignedTo = issue.assignedTo; _view.estimatedHours = isNaN(issue.estimatedHours) ? "" : issue.estimatedHours.toString(); _view.addEditLabel = issue.id == 0 ? "Add Issue" : "Save Issue"; } private function saveIssue(event:UIEvent = null):void { _issue.project = _view.project; _issue.description = _view.description; _issue.type = _view.type; _issue.severity = _view.severity; _issue.status = _view.status; _issue.details = _view.details; _issue.reportedOn = _view.reportedOn; _issue.reportedBy = _view.reportedBy; _issue.assignedTo = _view.assignedTo; _issue.estimatedHours = !isNaN(Number(_view.estimatedHours)) ?
#2
#3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
84
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Number(_view.estimatedHours) : null; _issueModel.saveIssue(_issue, new AsyncResponder(saveIssueResult, handleError)); } private function cancelChanges(event:UIEvent = null):void { selectedIssue = new Issue(); }
#4
private function changedIssue(event:UIEvent):void { selectedIssue = event.data; }
#5
private function saveIssueResult(resultEvent:ResultEvent, token:AsyncToken = null):void { selectedIssue = new Issue(resultEvent.result); var event:UIEvent = new UIEvent(UIEvent.SELECTED_ISSUE_SAVED); event.data = _issue; EventDispatcherFactory.getEventDispatcher().dispatchEvent(event); } private function handleError(event:FaultEvent, token:AsyncToken = null):void { Alert.show(event.message.toString()); }
#6
#7
} } 1 Get property for selected issue 2 Set property for selected issue 3 Save issue 4 Cancel changes 5 Selected Issue changed 6 Save issue result 7 Error handler
We then create get and set properties (#1 & #2) for the currently selected issue. The get property will simply return the current issue, while the set property has a little more responsibility.
The set property will update all of the form fields in the view so that they
match the currently selected issue.
Properties in ActionScript ActionScript, unlike Java has first-class support for properties similar to how other languages such as C#, Ruby and even Groovy. What this gives you is the ability to get and set values on a variable as if it were a public member variable on your class, instead of having to do getXXX() and setXXX() methods like you would in Java. The syntax for defining your get/set properties is as follows:
public function get name():String { return _name; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
85
} public function set name(value:String):void { _name = value; } The convention that many developers use is to prefix the private member variables with an underscore (_) so that the compiler can figure out if you are trying to reference the private member variable or the get/set property in your class. One of the advantages of using get/set properties is that it removes the need to define a bunch of getXXX/setXXX methods that do nothing beyond getting and setting the private variable. Having the use of properties allows you to simply define your member variables as public, then if you have a need to encapsulate some sort of logic when member variables are accessed, you can easily refactor your class to use a get/set property and none of the classes that use it will have to change as a result.
Next we've defined a method to handle saving our issue (#3), which gets the values from the view and updates the currently selected issue before calling the saveIssue method on the model. The method to respond to the "Cancel Changes" button being clicked (#4), simply sets
the
selected
issue
to
a
brand
new
Issue
object.
Whenever
the
SELECTED_ISSUE_CHANGED event gets fired, the changedIssue (#5) method gets called and sets the selected issue by using our set property we defined earlier. Lastly we create the handlers for our saveIssueResult (#6) and the error handler (#7) to handle the result from calling the issue model.
4.6.2 Updating the Issue Model Now that we've implemented the Presenter for our Details View, let's implement the new methods in our IssueModel to support the new functionality.
Listing 4.10 shows the code
necessary to implement the additional methods.
Listing 4.10 IssueModel.as package org.foj.model { ... public function getIssue(id:Number, responder:IResponder):void { var token:AsyncToken = _issueService.get(id); token.addResponder(responder); } public function saveIssue(issue:Issue, responder:IResponder):void { var token:AsyncToken = _issueService.save(issue); token.addResponder(responder); }
#1
#2
} ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
86
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
} 1. getIssue 2. saveIssue
Just like earlier, our methods in the IssueModel are fairly simple.
The getIssue (#1)
method simply takes an id parameter to find a specific Issue as well as an IResponder to add a responder to the token returned from the call to the WebService.
The saveIssue (#2)
method takes in an Issue object to pass along to the WebService to persist, as well as an IResponder.
4.6.3 Updating the Detail View The last part of the MVP triad that needs to be updated now is the DetailView. Listing 4.11 shows the updated version of the DetailView.mxml after we add the necessary methods to dispatch the events to save an Issue or cancel the changes.
Listing 4.11 DetailView.mxml
#1
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
87
var cancelEvent:UIEvent = new UIEvent(UIEvent.CANCELLED_ISSUE_EDIT); EventDispatcherFactory.getEventDispatcher().dispatchEvent(cancelEvent); } ]]> ...
#5 #6
1 creationComplete 2 initialization 3 saveChanges 4 cancelChanges 5 click event on saveChangesButton 6 click event on cancelChangesButton
First we tell the component to call the init (#2) method to initialize our component when the creationComplete (#1) event gets fired.
Inside of the init method, we bootstrap our
presenter just like we did earlier in the MasterView. We also set the selectedItem property on our three combo boxes to -1, so that they don't have a value selected when the component is first created.
Then we add an event handler for the click event on the
saveChangesButton (#5) called saveChanges (#3). Inside of the saveChanges method we simply dispatch an event signaling that the button has been clicked. Lastly we add an event handler cancelChanges (#4) for the click event on the cancelChangesButton (#6) and it also simply dispatches an event notifiying the application that the button had been clicked.
4.7 Enhancing the Comments View We're almost done enhancing our sample application. The last part of our application that needs to be updated is the Comments View.
We'll start by creating an MVP triad just as
before, then later on we'll also be adding a modal popup to add new comments as well as edit existing ones. So let's get started now.
4.7.1 Creating a Comments Presenter Creating the Presenter for the Comments View of the application will be just like the last two Presenters that we created.
The CommentsListPresenter, however is now going to be
responsible for maintaining a little bit more state than the previous Presenters. It will need to maintain a reference to not only the currently selected issue, but also the currently ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
88
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
selected comment, in order for us to properly persist the Comment objects.
Because the
code necessary for the CommentsListPresenter is rather lengthy, we'll break it up a bit like we did earlier with the DetailsPresenter earlier. Listing 4.12 shows the first section we'll look at, where we define what events this Presenter will listen for and a couple of get/set properties.
Listing 4.12 CommentsListPresenter.as package org.foj.presenter { ... public class CommentsListPresenter { private private private private private
var var var var var
_selectedIssue:Issue; _selectedComment:Comment; _commentModel:CommentModel; _view:CommentsView; _pop1:EditCommentForm;
#1
public function CommentsListPresenter(view:CommentsView = null) { this._commentModel = new CommentModel(); this._view = view;
#2
EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.SELECTED_ISSUE_CHANGED, changeSelectedIssue); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.DELETE_COMMENT_BUTTON_PRESSED, removeComment); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.SELECTED_COMMENT_CHANGED, changeSelectedComment); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.COMMENTS_UPDATED, refreshComments);
#3
} private function get selectedIssue():Issue { return this._selectedIssue; }
#4
private function set selectedIssue(issue:Issue):void { this._selectedIssue = issue; _view.addCommentButton.enabled = issue.id > 0; }
#5
private function get selectedComment():Comment { return this._selectedComment; }
#6
private function set selectedComment(comment:Comment):void { this._selectedComment = comment;
#7
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
89
_view.editCommentButton.enabled = comment != null; _view.deleteCommentButton.enabled = comment != null; } ... 1. 2. 3. 4. 5. 6. 7.
maintaining state constructor event listeners get property for selectedIssue set property for selectedIssue get property for selectedComment set property for selectedComment
First we start out by defining some variables to maintain our state for us (#1). Then we define a constructor (#2), and just like earlier, we take in a reference to the View that this Presenter is created for. We then initialize our Model, and add a few event listeners (#3) for the Presenter so it can react properly to the specific events it needs to react to. Once again we're leveraging get and set properties in this Presenter, using the set property to trigger whether or not certain elements of the View should be enabled. We start out by creating a get property for the currently selected issue (#4), and in it's corresponding set property (#5), we determine whether or not to enable the "Add Comments" button based on whether or not the currently selected issue's id property is greater than zero, meaning it has been saved. We do this to make sure that the user can't save a comment for an Issue that has not been persisted to the backend previously.
We create a get property for the
currently selected comment (#6) as well as a set property for the selected comment (#7). Similar to the last set property, we use this opportunity to enable or disable the "Edit Comment" and "Delete Comment" buttons based on whether or not there is a comment selected in the List component containing the comments.
Listing 4.13 CommentsListPresenter continued ... private function changeSelectedIssue(event:UIEvent):void { selectedIssue = event.data; selectedComment = null; refreshComments(); }
#1
private function changeSelectedComment(event:UIEvent):void { selectedComment = event.data; }
#2
private function removeComment(event:* = null):void { CursorManager.setBusyCursor(); _commentModel.removeComment(selectedComment.id, new AsyncResponder(removeCommentResult, handleError)); } private function refreshComments(event:* = null):void { CursorManager.setBusyCursor();
#3
#4
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
90
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
_commentModel.getCommentsForIssueId(selectedIssue.id, new AsyncResponder(loadCommentsResult, handleError)); } private function removeCommentResult(event:ResultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); refreshComments(); } private function loadCommentsResult(event:ResultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); var comments:ArrayCollection = new ArrayCollection(); for each (var result:* in event.result) { comments.addItem(new Comment(result)); } _view.commentsList.dataProvider = comments; _view.commentsList.selectedIndex = -1; selectedComment = null; } private function handleError(event:FaultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); Alert.show("Error occured: " + event.message); }
#5
#6
#7
} } 1. handler for selectedIssue changing 2. handler for selectedComment changing 3. removeComment 4. refresh comments list 5. removeCommentResult 6. loadCommentsResult 7. handleError
Listing 4.13 shows the rest of the code for our CommentsListPresenter. First we define a method to handle when the selected issue changes (#1), where we set the selected issue from the data passed along in the event. We then set the selected comment to null, and refresh the list of comments to be displayed. When someone clicks on a comment in the List view of the CommentsView, the selectedCommentChanged (#2) will get called, where we set the selected comment to the comment in the incoming event. The next method we define is a method to handle when the user clicks on the "Remove Comment" button (#3). We first tell the CursorManager to set the busy cursor, like we did earlier in the other Presenters. Then we make a call to our CommentModel to remove the selected comment. When the result comes back (#5) from our call to the CommentModel we remove the busy cursor and tell the Presenter to refresh the list of comments for our View. To refresh the list of comments we created a function called refreshComments (#4), which makes a call to our CommentModel to retrieve a list of comments for a given issue id. When the result comes back from this call (#6), we iterate through the list of ObjectProxy ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
91
instances that come back from the WebService and use the convenience constructor we defined at the beginning of this chapter to create a list of Comment objects. We then set the dataProvider property of the List component in the View to this list of Comment objects and remove the busy cursor. Lastly we create a very simple error handler (#7) like we did for the two previous Presenters that we created earlier.
4.7.2 Creating a Comment Model The CommentModel, other than the method names will look very similar to the IssueModel we created earlier. Listing 4.14 shows the code for the CommentModel.
Listing 4.14 CommentModel.as package org.foj.model { import mx.rpc.AsyncToken; import mx.rpc.IResponder; import mx.rpc.soap.WebService; public class CommentModel { private var _commentService:WebService; public function SoapCommentModel() { _commentService = new WebService(); _commentService.wsdl = "http://localhost:8080/services/CommentService?wsdl"; if (_commentService.canLoadWSDL()) { _commentService.loadWSDL(); } }
#1
public function getCommentsForIssueId(issueId:Number, #2 responder:IResponder):void { var token:AsyncToken = _commentService.findCommentsByIssueId(issueId); token.addResponder(responder); } public function removeComment(id:Number, responder:IResponder):void { var token:AsyncToken = _commentService.remove(id); token.addResponder(responder); }
#3
} } 1 Constructor 2 getCommentsForIssueId 3 RemoveComment
First in the constructor (#1) we create a new WebService component and set its wsdl property to the WSDL for our web service.
Then just like we did earlier we tell the
WebService to load the WSDL so we can make calls to it. Next we define the two methods
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
92
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
we need in order for our application to function, the getCommentsForIssueId (#2) and the removeComment (#3) methods.
4.7.3 Comment View Now that we have the methods implemented in the CommentModel that we need, we can go ahead and update the CommentsListView component to enable it to respond to user interaction.
Listing 4.15 shows our updated CommentsListView.mxml file.
For right now,
we're only going to respond to clicks in the List component containing the comments and the "Remove Comment" button, then in the next section we'll start implementing the modal popup that will allow us to add new comments and edit existing comments.
Listing 4.15 CommentsListView.mxml
#1
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
93
]]>
#5
#6
1 creationComplete 2 init 3 removeComment 4 selectedItemChanged 5 itemClick event 6 click event
Again, we use the creationComplete event (#1) to call our init method (#2) where we bootstrap our Presenter. We then create an event handler named removeComment (#3) for when the user clicks the "Delete Comment" button (#6). Inside of this method we set the selectedIndex to -1 so that no items are selected in the List and dispatch an event notifying the rest of the application that the button has been clicked. Next we define an event handler method selectedItemChanged (#4) for when the user selects an item in the List component (#5).
4.8 Adding a popup form for editing Comments We're almost done with this iteration of our sample application. All that's left to do now is to create a popup for adding and editing comments. Figure 4.2 shows a wireframe mockup of what we want our popup to look like. It's a fairly simple popup dialog that will consist of a text input for the author's name, the date the comment was created, and a text area to enter the details of the comment into.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
94
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 4.2 Wireframe of popup for editing comments
Now that we have an idea of what it is that we want to build, let's get started. The following section will illustrate exactly how we're going to do that, starting with updating the Comment Presenter.
4.8.1 Updating the Comment Presenter First let's add the methods we need to the CommentPresenter for our popup dialog. Listing 4.16 shows the code we need to add.
Listing 4.16 Adding popup private var _pop1:EditCommentForm;
#1
public function CommentsListPresenter(view:CommentsView = null) { #2 ... EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.ADD_NEW_COMMENT_BUTTON_PRESSED, addNewComment); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.EDIT_COMMENT_BUTTON_PRESSED, editComment); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.SAVE_COMMENT_BUTTON_PRESSED, saveComment); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.CANCEL_EDIT_COMMENT_BUTTON_PRESSED, cancelEdit); EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.COMMENTS_UPDATED, refreshComments); ... } ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
95
private function addNewComment(event:* = null):void { #3 selectedComment = new Comment(); selectedComment.issue = selectedIssue; _pop1 = PopUpManager.createPopUp((_view as UIComponent).root, EditCommentForm, true) as EditCommentForm; PopUpManager.centerPopUp(_pop1 as UIComponent); } private function editComment(event:* = null):void { #4 _pop1 = PopUpManager.createPopUp((_view as UIComponent).root, EditCommentForm, true) as EditCommentForm; PopUpManager.centerPopUp(_pop1 as UIComponent); _pop1.author.text = selectedComment.author; _pop1.commentDate.selectedDate = selectedComment.createdDate; _pop1.commentText.text = selectedComment.commentText; } private function saveComment(event:* = null):void { #5 CursorManager.setBusyCursor(); selectedComment.author = _pop1.author.text; selectedComment.createdDate = _pop1.commentDate.selectedDate; selectedComment.commentText = _pop1.commentText.text; _commentModel.saveComment(selectedComment, new AsyncResponder(saveCommentResult, handleError)); } private function cancelEdit(event:* = null):void { removePopUp(); }
#6
private function saveCommentResult(event:ResultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); selectedComment = event.result as Comment; var commentsChangedEvent:UIEvent = new UIEvent(UIEvent.COMMENTS_UPDATED); EventDispatcherFactory.getEventDispatcher() .dispatchEvent(commentsChangedEvent); removePopUp(); } private function removePopUp():void { PopUpManager.removePopUp(_pop1 as UIComponent); } ...
#7
#8
1 the popup component 2 add more event listeners 3 addNewComment 4 editComment 5 saveComment 6 cancelEdit 7 saveCommentResult handler 8 removePopUp
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
96
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
The first thing we need to change in the CommentPresenter is to declare a member variable for the popup itself (#1).
Next we add a few more event listeners (#2) to respond
to user clicks on the buttons on the CommentsView and the popup we're going to create in a minute. The first event handler we'll define is an event handler for when the "Add New Comment" (#3) button is pressed.
We first set the selected comment to a brand new comment and
then create the popup using the PopupManager.
The call to createPopUp takes three
parameters; the component that will be the parent for the popup, what class the popup should be, and whether or not the popup should be modal. We set the parent for the popup to be the root application component so that when we call centerPopUp on the PopUpManager in the next line it will center relative to the whole application instead of to the CommentView. The next event handler, editComment (#4) starts out just like we did for the addNewComment handler. We create a popup using the PopUpManager and center it, and then we update the fields in the popup with the values from the currently selected comment. When the user clicks on the "Save Comment" button in our popup, the saveComment (#5) handler will get called. We then take all the values from the popup and update our currently selected comment and make a call to the CommentModel to save the comment. When the user clicks on the "Cancel Changes" button on the popup, the cancelEdit (#6) method will get called, where we simply remove the popup. When the result comes back from the call to save the comment, the saveCommentResult handler (#7) gets called. In there we update the selected comment with the result from the web service call and then trigger the List to update by dispatching an event signaling that the comments have changed and remove the popup (#8).
4.8.2 Updating the Comment Model Next we have another method we need to add to our CommentModel to call the web service and save a comment.
Listing 4.17 CommentModel.as ... public function saveComment(comment:Comment, responder:IResponder):void { var token:AsyncToken = _commentService.save(comment); token.addResponder(responder); } ... Listing 4.17 shows the method we need to add to the CommentModel to save comments. This method is very simple just like every other call we've made to our WebService.
It
simply delegates the call to the web service and adds a responder to the token so our application can react when the result comes back from the asynchronous call to the web service. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
97
4.8.3 Creating the popup component Now we can create the actual popup component that we'll use to create new comments as well as edit existing ones.
Even though we're instantiating our popup in ActionScript by
making calls to the PopUpManager, it's easiest for us to define this actual component in MXML just like all of our other View components.
Listing 4.18 shows the contents of our
EditCommentForm.mxml file defining the popup.
Listing 4.18 EditCommentForm.mxml
#1
#4
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
98
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#5
1 Extends TitleWindow 2 saveComment eventHandler 3 cancelChanges eventHandler 4 Form 5 ControlBar
The code for our popup looks very similar to the DetailsView for our issues. The main difference being that this component extends the TitleWindow (#1) component instead of the Panel component. Most of the popup windows you'll create will extends this component. We then define a couple of methods (#2 & #3) to react to the button clicks of the popup to dispatch UIEvents for our application to listen for. Just like our DetailView, we use the Form component (#4) for convenient layout of the form fields and labels for our popup. We then create a ControlBar (#5) to hold the "Save Comment" and "Cancel" buttons.
4.8.4 Update CommentsListView The only thing left now is to update the CommentsListView to dispatch the necessary events for the "Add Comment" and "Edit Comment" buttons, which is shown in Listing 4.19.
Listing 4.19 Updated CommentListView.mxml ... private function addNewComment():void { var addNewCommentEvent:UIEvent = new UIEvent(UIEvent.ADD_NEW_COMMENT_BUTTON_PRESSED); EventDispatcherFactory.getEventDispatcher() .dispatchEvent(addNewCommentEvent); } private function editComment():void { var editCommentEvent:UIEvent = new UIEvent(UIEvent.EDIT_COMMENT_BUTTON_PRESSED); EventDispatcherFactory.getEventDispatcher() .dispatchEvent(editCommentEvent); } ... ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
99
1. addNewComment 2. editComment 3. click handlers on the buttons
The first two changes we need to add to the CommentsListView is to add a couple of methods (#1 & #2) to be called when the "Add New Comment" and "Edit Comment" buttons are clicked. These will simply create and dispatch UIEvents just like every other view we've defined so far. Then we need to update the buttons themselves (#3) so that they will call our methods we defined when they are clicked.
4.9 Summary In this chapter we explored the architectural elements that make up for a well designed, flexible and maintainable rich Internet application. We learned about how to apply the Model View Presenter pattern to our code and how following that pattern, we effectively separate the areas of concern into their own isolated parts.
We also discovered how once we
abstracted our calls to external services in our Model, how easy it would be to potentially replace this portion of the code without any other part of the code even having to know about it. In the next chapter we'll build upon what we've assembled in this chapter to leverage parts of the Mate framework that will give us a means to help manage the dispatching and handling of events by using an Event Map.
We'll also take advantage of the dependency
injection features to make the design we started in this chapter even more robust and flexible than it already is.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
100
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
5 BlazeDS Remoting and Logging
This introduction covers
Introducing and Getting BlazeDS
Building a BlazeDS Configuration Module
Flex and Java communication with BlazeDS
Spring BlazeDS Integration framework
Logging Events and Performance Statistics
In prior chapters we built up a Flex client application using the MVP design pattern and connected it up to Java through web services. Now we are going to move from XML/HTTP Web Services and try out remoting. For Action Message Format communication, or “AMF”, Flex provides the RemoteObject component that uses Adobe's own AMF binary protocol to communicate with the server. This means that you'll need some sort of process running on the server side, such as BlazeDS, that understands how to serialize and deserialize the AMF protocol. In the next section we will introduce you to BlazeDS before we begin getting BlazeDS and getting it to work with a sample application. We will be using the Flex-bugs sample application but it would be equally as good to wire up your own slice of functionality to see it work for your own interest.
5.1 Introducing BlazeDS A Flex client typically runs in a web browser or through the Adobe AIR desktop application platform. BlazeDS comes into the technology stack when the client would need to communicate with a Java application on the server that is commonly powered by a service oriented architecture (SOA). Figure 5.1 shows the basic anatomy of a BlazeDS application framework. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
101
Figure 5.1 The BlazeDS architecture
In a nutshell, BlazeDS is a collection of Java components that you can deploy with your web application for AMF/HTTP communication between Flex and Java that also supports messaging. BlazeDS is basically a subset of the commercial Flex Data Services offering from Adobe, and while you can do most of the things you need to with BlazeDS there are certain things like clustering and advanced binding techniques that can only be accomplished with the full blown Flex Data Services.
BLAZEDS IN ADOBE'S WORDS BlazeDS is the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Adobe® Flex® and Adobe AIR™ applications for more responsive rich Internet application (RIA) experiences.
So right about now you may be asking yourself, "Why would I want to do that?" One of the biggest advantages to using the AMF protocol for communication is performance. Adobe's James
Ward
put
together
a
really
nice
application
called
BlazeBench
(http://www.jamesward.com/wordpress/2007/12/12/blazebench-why-you-want-amf-andblazeds/), which benchmarks the various methods of communicating to the server side from a web application, not just Flex, and displays the different aspects of the transaction. Figure 5.2 shows a sample output from BlazeBench. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
102
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 5.2 Comparing performance for various methods of transferring data from the server side to the client.
It may be a little difficult to see in the screenshot in Figure 5.2, but the total execution time for an operation using Flex AMF3 is by far quicker for round trip time to the server as well as the smallest in data bandwidth used for the transmission.
5.2 Getting BlazeDS You're in luck if you are using Maven to build your web application. After much persistence from the development community, there are now BlazeDS jars in the main Maven repository. You can simply add the dependencies shown in Listing 5.1 in your pom.xml to include the BlazeDS libraries in your application and they get downloaded for you.
Listing 5.1 Adding BlazeDS to your pom.xml com.adobe.blazeds blazeds-core 3.2.0.3978 com.adobe.blazeds blazeds-common 3.2.0.3978 com.adobe.blazeds blazeds-proxy 3.2.0.3978 ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
103
com.adobe.blazeds blazeds-remoting 3.2.0.3978 org.springframework.flex spring-flex 1.0.0.RELEASE You may have noticed that we've also included the spring-flex artifact. This is because we are going to use this framework for communicating with our server side destinations. The Spring BlazeDS Integration reduces the complexity in configuring BlazeDS. For those of you not using Maven, you have to jump through a couple of hoops to get the proper libraries in your application. BlazeDS
Adobe doesn't provide the jar files directly on the downloads
page
(http://opensource.adobe.com/wiki/display/blazeds/Release+Builds), they only provide the binary distribution as a WAR file, a turnkey solution which contains a ready-to-use Tomcat instance with some sample applications, and a source download. In order to get the jars the easiest way is to get the WAR distribution and extract them out of the WEB-INF/lib directory. Thankfully everything you need to add to your web application is there including all the dependencies you need.
Figure 5.3 shows the libraries contained in the WEB-
INF/lib directory you'll need.
Figure 5.3 BlazeDS libraries and their dependencies
Now let's get BlazeDS connected up in a more modular way with Maven.
5.3 Building a BlazeDS Configuration Maven Module In order to share configuration depenencies between the Java web application and the Flex client we are going to create a BlazeDS configuration module. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
104
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 5.4 BlazeDS configuration module project structure
We can create this module at the same level as the webapp and the Flex client. The directory structure will look like the one in figure 5.4. As you can see the only thing that we will need will be the resources directory for the BlazeDS configuration files and the pom descriptor and a maven assembly. Remember that the project structure can be generated much like we demonstrated in chapters 1 and 2. Without out generating them with the Maven archetype you must specify in the top-level pom (Flex-Bugs) a new module and we must associate the BlazeDS configuration as a dependency for the other projects. Next we want to modify the module's pom descriptor file as seen in listing 5.2.
Listing 5.2 BlazeDS configuration module pom flex-bugs org.foj 1.0-SNAPSHOT 4.0.0 org.foj flex-bugs-blaze-config pom 1.0-SNAPSHOT org.apache.maven.plugins maven-assembly-plugin Package BlazeDS configuration single
#1
#2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
105
package #3 src/main/assembly/resources.xml #4 1 Coordinates indicate pom packaging 2 Utilizing the maven assembly plugin 3 Assembly happens during package phase 4 Path to assembly instructions
Since we created this module only to share its resources between the client and server modules we can specify a pom packaging type #1 and that we have chosen to utilize the maven-assembly-plugin #2 to zip up our resources #4 during the package phase #3. This pom basically instructs maven to simply zip up the resources we need to share into a zip file without having to do the other typical phases and goals that would be involved in compiling and creating a jar assembly.
Maven Assembly Install and Deploy The maven framework automatically installs and deploys anything built through the context of an assembly. For the install process, maven installs the artifact(s) into your local repository located typically in the .m2 directory. This is usually found in your home directory. Maven deploys artifacts if the distribution management is specified with snapshot and/or release repositories defined. For deployment a valid Maven repository is required like Sonatype's very own “Nexus” repository.
Now let's take a quick peek at listing 5.3 for the details needed to include the right files into some format.
Listing 5.3 Assembly descriptor for configurmation module resources zip false
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
106
Allmon and Anderson / Flex on Java
src/main/resources
Last saved: 1/6/2010
#2
1 The assembly format 2 What to include in the assembly.
This assembly instructs Maven to create a zip file #1 with the files listed in the fileset #2. That is the location where we will be adding our two BlazeDS configuration files that need to be shared across modules.
5.3.1 Configuring BlazeDS BlazeDS makes use of a couple key config files that by convention get put in a flex folder under WEB-INF. You could probably put them wherever you like, but by putting them under the WEB-INF folder, they can only be accessed by your web application and you can feel safe knowing any sensitive information, such as passwords in your proxy-config.xml, are safe from prying eyes. The first file we'll look at is the services-config.xml. Listing 5.4 shows the contents of our services-config.xml file.
Listing 5.4 services-config.xml
#1
#2
1 Import the proxy-config.xml
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
107
2 Channel definition 3 Endpoint url with property names
This is what a basic services-config.xml file should look like for BlazeDS.
There are a
couple of sections to note here. First we tell the services-config.xml file to import the
proxy-config.xml #1 and where to find it. Next in the channels definition #2 we define an AMF channel and point it at a servlet that we'll define in our web.xml later. It's also possible to use properties for the channel definition that transform into their real values when the application starts. It's common to do this for the endpoint url attribute #3. Using the {server.name} and {server.port} properties allow the application to be ported without having to hard code different static values each time it needs to be on a different box.
You
can
refer
to
the
BlazeDS
documentation
at
http://livedocs.adobe.com/blazeds/1/blazeds_devguide/ if you need more information on how to tweak the configuration further. The last configuration file we'll be concerning ourselves with for now is the proxy-
config.xml shown in Listing 5.5. Listing 5.5 proxy-config.xml 100 2 true
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
108
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
The proxy-config.xml file is where we can configure any server side proxies that we need to leverage if for instance we're communicating with web services outside of our domain and have no control over placing a crossdomain.xml file on the server. With BlazeDS configured and ready to go, we can now get on with the tasks of modifying some of our services to communicate via AMF.
Where's the remoting-config.xml? Great question! The BlazeDS examples in this book take advantage of a brand new Spring BlazeDS Integration framework brought to us by SpringSource. Spring BlazeDS Integration allows us to set up remoting destinations by simply annotating a Java class and specific method that need to be exposed to Flex through remoting. You can still choose to hand wire the xml instead of using the annotations but we recommend the use of the Spring BlazeDS Integration annotations and will demonstrate them in this chapter.
In the next sections we're going to start off simple by showing you how to connect to a simple Plain Old Java Object (POJO) on the server side and then get a little more complicated and leverage the powerful Spring Framework to communicate with a fully injected Spring bean. ADD THE BLAZEDS CONFIG MODULE TO THE TOP-LEVEL POM To configure the top-level pom open and edit the pom.xml file in the Flex-Bugs directory and add the flex-bugs-blaze-config module like in listing 5.6.
Listing 5.6 Adding BlazeDS config module to the top-level pom.xml flex-bugs-blaze-config flex-bugs-ria flex-bugs-web Next we need to associate the dependency with the webapp and Flex client. UPDATE THE FLEX AND JAVA WEB MODULES Now we are able to add the configuration module to the other projects as a dependency by editing their respective pom files as seen in Listing 5.7 for the flex-bugs-web pom.
Listing 5.7 Adding the BlazeDS config depenendcy to the Java Web module org.foj flex-bugs-blaze-config 1.0-SNAPSHOT resources provided zip
#1
#2 #2 #3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
109
. . . 1 The BlazeDS configuration module dependency declaration 2 Utilize the resources classifier and provided scope 3 The type element is the type of artifact produced by the assembly
As you can see, we can simply add the new dependency to the dependencies element #1 in each module's pom and we are done there. Since the scope is provided it will not add the resources #2 to the war. We had to specify the type #3 because it's a zip and not the default type. We will actually be getting them from the Flex module by editing the pom descriptor in the flex-bugs-ria module as seen in listing 5.8.
Listing 5.8 Adding the BlazeDS config depenendcy to the Flex module org.apache.maven.plugins maven-dependency-plugin unpack-config unpack-dependencies generate-resources ${project.build.directory}/generatedresources flex-bugs-blaze-config ${project.groupId} true
#1
#2 #3
#4 #5
1 The maven-dependency-plugin definition 2 Invoke the unpack-dependencies goal 3 Execute the goal during the generate-resources phase 4 Output directory to unpack the zip file 5 Include the flex-bugs-blaze-config artifact
There's many ways to share configuration and as you can see we can use the mavendependency-plugin #1. We execute the unpack-dependencies goal #2 during the generateresources phase #3 and must choose an output directory #4 in order to unzip the file where we
desire.
You
may
have
noticed
that
we
used
the
built-in
maven
property
${project.build.directory} which equates to the location of the target directory. The only required element left is what to unpack. This is where we specify the flex-bugs-blaze-config artifact #5.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
110
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Once the configuration completed for the shared configuration approach we are now ready to start integrating Flex with Java through the use of BlazeDS AMF remoting!
5.4 Exposing Java Services to Flex Remoting In this section we will demonstrate how to configure and expose Java services to Flex through the use of BlazeDS and Spring annotations. First we will modify the web module configuration and then modify the Java code. For demonstration purposes we will use the Flex-Bugs sample application.
5.4.1 Web Module Configuration Updates First we'll modify the Web module's configuration to enable it for remoting. We'll start with the applicationContext.xml file located in the /flex-bugs-web/src/main/webapp/WEB-INF directory and as seen in listing 5.9.
Listing 5.9 applicationContext.xml
#2 #3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
111
1 Update the schema namespace 2 Enable annotations 3 Use the component scanner
In order to take advanatage of the Spring BlazeDS Integration we must add the appropriate schema changes and namespace elements #1. Since we will be using annotations to expose our services to BlazeDS remoting destinations we must enable annotations #2 and use the component scanner #3 to help Spring find the annotations we are going to define. Next, by convention we must provide the flex-spring-servlet.xml configuration file, as seen in listing 5.10, because the framework depends on it.
SPRING BLAZEDS CONVENTION The naming of the flex-spring-servlet.xml file is important because the framework scans for it by convention and the application will fail to startup properly without it.
Later, we will use the flex-spring-servlet.xml file to help configure the JMS service.
Listing 5.10 flex-spring-servlet.xml plumbing --> Lastly, we need to add a new servlet definition to the web.xml so that your Flex application can communicate with the server side application. Listing 5.11 shows the changes that need to be made to the web.xml of our web application module.
Listing 5.11 web.xml flex-spring org.springframework.web.servlet.DispatcherServlet 1 flex-spring /messagebroker/*
#1 #2
#3
1 Servlet name 2 Servlet class 3 servlet mapping definition
This is a typical Java web application servlet mapping where you define a servlet name #1, a servlet class #2 to dispatch actions to whenever a URL pattern #3 match is found. Now let's begin actually working with code by modifying the server side classes.
5.4.2 Expose AMF Remoting Destinations Connecting to POJOs on the server side is rather trivial once you've got BlazeDS configured properly.
In order to illustrate connecting to a POJO from Flex, we're going to use the
FlexBugs application by exposing the IssueManager interface to Flex remoting using the Spring BlazeDS Integration framework. UPDATES TO ISSUE CODE Listing 5.12 shows the code for the IssueManagerImpl that we'll be connecting to.
Listing 5.12 IssueManager.java package org.foj.service; import org.foj.model.Issue; import org.springframework.flex.remoting.RemotingDestination; import org.springframework.flex.remoting.RemotingInclude;
#1
import javax.jws.WebService; @WebService @RemotingDestination(channels = {"my-amf"}) public interface IssueManager { @RemotingInclude
#2
#4
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
113
java.util.List getAll(); @RemotingInclude Issue get(Long id); @RemotingInclude Issue save(Issue issue); @RemotingInclude void remove(Long id); }
1 Import the Spring BlazeDS Integration classes 2 RemotingDestination exposes the issueService to Flex remoting 3 RemoteInclude annotation
As you can see not much had to change to expose the issueService methods to Flex. The first thing we did was import the Spring BlazeDS Integration classes needed #1. The Spring classes help us by giving us the RemotingDestination and RemotingInclude annotations to use. Next, we added the remotingDestination “my-afm” #2. The remotingDestination must match the one defined in the services-config.xml BlazeDS configuration file. This annotation actually exposes this class as a destination for any Java class that implements the IssueManager interface. This means that the IssueManagerImpl Java class, the class that implements the IssueManager, will have it's “issueService” exposed over BlazeDS remoting and not just XML/HTTP. This is because IssueManagerImpl has a WebService annotation named issueService that describes IssueManager as the endpointInterface. The Spring BlazeDS Integration framework removed the need for having to define this metadata through the remoting-config.xml configuration file. In fact, we don't even use a remoting-config.xml file with the BlazeDS Integration framework. Once those things are in place we are able to simply annotate each method that we need to expose methods with the @RemotingInclude #3 annotation and we are done! The @RemoteInclude annotation exposes the method over the RemotingDestination. If needed, the Spring BlazeDS Integration framework's remoting package also provides a @RemotingExclude annotation for intentionally excluding methods. Now let's move on to making the necessary changes to the Flex client in order to take advantage of the exposed AMF service. UPDATES TO COMMENTS CODE Now that we have things in place for the Issue code we will need to make the same types of changes
to
the
CommentManager
interface.
Code
listing
5.14
represents
the
CommentManager.java class changes.
Listing 5.14 CommentManager.java package org.foj.service; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
114
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
import org.foj.model.Comment; import org.springframework.flex.remoting.RemotingDestination; import org.springframework.flex.remoting.RemotingInclude; import javax.jws.WebService; import java.util.List; @WebService @RemotingDestination(channels = {"my-amf"}) public interface CommentManager { @RemotingInclude List findCommentsByIssueId(Long issueId); @RemotingInclude void deleteAllCommentsForIssueId(Long issueId); @RemotingInclude Comment get(Long id); @RemotingInclude Comment save(Comment comment); @RemotingInclude void remove(Long id); } As you can see there's nothing
different that we had to do
to expose the
CommentManager to remoting. All we had to do was add the necessary imports, include the RemotingDestination, and add the appropriate RemotingInclude annotations for the methods we need exposed. Now we have a Java server side that is set up for BlazeDS remoting. We were able to tackle nearly all of the Java configuration through Spring's robust Flex BlazeDS Integration framework and its' extremely useful annotations.
5.5 Connect to Java with BlazeDS With the Java remoting services ready to go we can begin the work on the Flex client to connect to the exposed service objects and methods. Let's start with the Issue.as object as seen in listing 5.13.
Listing 5.13 Issue.as package org.foj.dto { [RemoteClass(alias="org.foj.model.Issue")] public class Issue { ...
#1
1 RemoteClass annotation
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
115
As you can see, that wasn't very complicated. We only had to associate this obect with the server side equivalent by using the RemoteClass annotation #1. We need the RemoteClass annotation because it allows us to map domain objects between Flex and Java by specifying the Java package and class name.
FLEX REMOTECLASS ANNOTATION Any attempts at persisting Issue objects will fail without correctly specifying the RemoteClass. This is because BlazeDS will not know what server side object the Flex object is bound to.
Next we need to modify the IssueModel.as class to call our POJO as shown in Listing 5.14.
Listing 5.14 IssueModel.as package org.foj.model { import import import import import import
mx.rpc.AsyncToken; mx.rpc.IResponder; mx.rpc.events.FaultEvent; mx.rpc.events.ResultEvent; mx.rpc.remoting.RemoteObject; org.foj.dto.Issue;
public class IssueModel { private var _issueService:RemoteObject; public function IssueModel() { _issueService = new RemoteObject(); _issueService.destination = "issueService"; }
#1
#2
public function getIssues(responder:IResponder):void { var asyncToken:AsyncToken = _issueService.getAll(); asyncToken.addResponder(responder); } ... } 1 Create the RemoteObject 2 Set the destination found in the
Again we see a few things have to change and yet there's only been minor changes required to take advantage of the performance increases of BlazeDS remoting. In the IssueModel we are now using the Flex RemoteObject #1 for communication to the issueService instead of the WebService object. When we construct a new IssueModel we set ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
116
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
the RemoteObject destination #2 as the issueService available to us by the server side. That's it really!
RemoteObject instance names It's important to point out that the name of the RemoteObject instance variables map to the bean name in the Spring applicationContext.xml file. Be sure these names are correct!
With all of that out of the way you should be able to run your application and see that it's now populating the Issues DataGrid with data from our issueService through BlazeDS rather than the Web Service created in Chapter 3.
5.6 Logging Since the inception of programming developers have always found ways to enable the applications for communicating what they are doing. This comes in all kinds of forms including anything from logging basic information to fatal exceptions during the application runtime. With the Flexbugs sample application we have a need for logging the Java server side events, the BlazeDS specific events, and also gather some performance statistics. Building the FlexBugs application with Appfuse simplified this task by configuring the very popular log4j framework for logging Java application messages. However, now we must inform the log4j framework that we would like to log even more for BlazeDS.
5.6.1 BlazeDS Logging Adding BlazeDS into the mix was a good thing. But adding any library or framework, for the good of the application, presents new challenges and we must collect information from it use to better understand what is happening when we use it. Thankfully, SpringSource recognized this with the Spring BlazeDS Integration framework and added a new logging object to it's core package. The CommonsLoggingTarget class allows for automatic logging of events from BlazeDS into the applications existing logging configuration. CONFIGURE LOG4J FOR BLAZEDS FILE LOGGING There are a couple places where we have to configure an application to port out messages to the console or even a log file. The first place, as seen in listing 5.15 is the log4j.xml configuration file found in the flex-bugs-web module. It can be found in the /flexbugs/flexbugs-web/src/main/resources directory.
Listing 5.15 log4j.xml
#1 #2 #3 #4
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
117
#5
#6 #7 #8 #9
1 log4j rolling file appender 2 log file name 3 log file layout definition 4 ConversionPattern configuration 5 ConsoleAppender configuation 6 blazeds logger definition 7 logging level 8 file appender 9 console appender
As seen in listing 5.15 the log4j configuration can be very trivial and yet extremely robust. Log4j itself allows for outputing to both the console and to a file. In our example we specified a RollingFileAppender #1 so that we can keep our log messages and continue to add to that file as we use the application. We must specify the log file name as flexbugs.log #2. Log4j requires the File parameter and doesn't provide a default for it. It's also possible to specify a path to the file in this parameter. It will place the flexbugs.log file at the root of the flex-bugs-web directory. Log4j also enables for various layout styles. By choosing to use the PatternLayout #3 we are able to specify what we want our messages to look like and even what information it should present in the log message. We do this by defining the ConversionPattern parameter #4. There are other patterns to choose from but we chose this one to give us some additional flexibility. The first thing to notice about the log message pattern is that it will prefix each message with the name “[flex-bugs-web]”. It then assigns some other parameters for what to print out in the message. For more on Log4j configuration take a peak at http://www.scribd.com/doc/7679107/Log4j-Quick-Reference. We also include an appender for the console #5. Console appending is especially helpful during the act of software development. Files are both really valuable for not just development but also for production environments. Finally, the last thing we need to do in the log4j.xml configuration is to create the logger itself. Here we created a logger named “blazeds” #6. We configured the logger to output at the DEBUG level #7 and append the messages to both our RollingFileAppender and the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
118
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
ConsoleAppender by their respective reference names “FILE” #8 and “CONSOLE”. That's it! Log4j is now ready to create a log file for the flexbugs application. CONFIGURE BLAZEDS TO OUTPUT TO LOG4J Setting up the logging of BlazeDS is a two-step process. Now that we have a logger available specifically for BlazeDS we can now configure BlazeDS to work with Log4j. Listing 5.16 shows the necessary changes to the services-config.xml file. This file is located in the /flexbugs/flex-bugs-blaze-config/src/main/resources directory.
Listing 5.16 services-config.xml ... flexbugs ...
#1 #2 #3
1 Logger class 2 Logging level 3 Category prefix
The BlazeDS configuration is really simple. All we really have to do is specify the Spring framework's CommonsLoggingTarget class #1, the logging level we desire #2, and the categoryPrefix
which
points
to
the
log4j.xml
logger
named
“flexbugs”
#3.
The
CommonsLoggingTarget can be configured to output more or less information from BlazeDS events through it's properties and logging categeories. It does this through the use of filters.
BlazeDS/LCDS logging categories There are several categories available for logging only the events you feel are worth logging. Logging can be performance intensive so it's wise to only log absolutely what you need to in order to provide the best support for the application. For more on the categories available or to know what things you can filter out of the logs see the API documentation for the CommonsLoggingTarget at http://static.springsource.org/springflex/docs/1.0.x/javadoc-api/org/springframework/flex/core/CommonsLoggingTarget.html
Now that logging has been configured for BlazeDS it's time to see the results. VIEW THE FLEXBUGS LOG FILE If you fire up your sample application you will find that there will be a log file available now. If you are using the flexbugs samples and ran the command “mvn clean jetty:run-war” on the flex-bugs-web module, after performing a “mvn clean install” on the flex-bugs-config module, you will now find the log file in the /flex-bugs/flex-bugs-web directory. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
119
Figure 5.5 shows the result of creating the logger from a console perspective.
Figure 5.5 Console log of Blazeds events
If you look toward the bottom of figure 5.5 you will find an AMF log event showing an issue completed for adding the BlazeDS logging to the log4j configuration. In that event you can see that the issue was Serialized through AMF/HTTP along with all the issue details. If you open the log file you will find the same results and logging of the BlazeDS events.
5.6.2 Built-in BlazeDS Benchmarking One nice feature of BlazeDS is it's built-in ability to aquire performance results on it's processing of messages. In this example we will configure the BlazeDS services-config.xml to retrieve the message times and size. These measurements are great for development but shouldn't be enabled for production because they do cause some performance degrade. This may seem counter-productive but it's not. It's just that it's good to assure that you have the best performance possible for your production environments at all times to keep the users delighted. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
120
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Logging affects performance A developer must choose wisely the messages that are logged in a production environment. We shouldn't keep log messages in an application that were only troubleshooting messages or sloppy in loop count logging that spits out a bunch of unimportant things for every element in a collection of objects. To put it simply, logging affects performance, especially when writing out to file IO. Therefore, make sure that debugging is not enabled in production unless it's absolutely necessary. Also be sure to only log INFO type messages when they are truly helpful or required.
ENABLE BLAZEDS PERFORMANCE COLLECTION In order to enable BlazeDS performance collection open up the services-config.xml for editing. Listing 5.17 shows the changes to the “my-amf” configuration.
Listing 5.17 services-config.xml true #1 true #2 1 Message times configuration 2 Message sizes configuration
In listing 5.17 we enabled the performance tracking properties for recording both message times and sizes. It's a simple boolean that can be set to false for production environment configuration. Adding these properties allow for the automatic logging of this information. This is a great way to get some insights on the objects your sending back and forth from the client to the server and back again. If you were to create a new issue in Flexbugs you would see something like the following in figure 5.6.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
121
Figure 5.6 BlazeDS MessagePerfomanceInfo in action
As seen in figure 5.6 we now have an additional log message directly under the insertion of a new Issue for the project “FlexBugs.” The BlazeDS MessagePerformanceUtils class shows the sendTime and the messageSize that we defined in the services-config.xml. We can see that the sendTime is equal to 1.25ms and a message size of 835.0b. In general, the Flex flex.messages.messaging.MessagePerformanceUtils class provides various metrics that describe the size and timing of a message sent from a client to the server and it's server response message. Performance information can be captured when the and are configured to true. From there it's possible to set up more performance gathering metrics in the client. Configuring the client would mean creating a response acknowledgement or message handler.
PERFORMANCE TESTING CONSIDERATION ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
122
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
The BlazeDS development guide instructs developers to not activate more than one performance measurement at a time because of the extra performance hit that each measurement adds to the application run time. So, use them wisely. If you truly have performance issues you may consider other means for performance testing like an opensource tool or commercial alternative. Using a separate tool for performance testing would allow the BlazeDS configuration to stay true to it's production environment configuration during the performance testing.
For more on how to configure message performance logging through a message event handler
refer
to
the
BlazeDS
development
guide
at
http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=mpi_3.html.
5.7 Summary In this chapter we refactored out the XML/HTTP Web Service implementation with BlazeDS AMF Remoting. The Web Service implementations had the least impact on the server side but require a little more work to be done on the client side, since we're not receiving first class objects in the responses.
When we switched to using BlazeDS to communicate with the
server side we gained in performance without increasing the complexity of the application. Using the Spring BlazeDS Integration framework reduced the amount of configuration typically required to set up remoting destinations. It also gave us a handful of very useful annotations and other features that reduced the complexity of the application even more. As we will continue to see throughout the remainder of the book, the Spring BlazeDS Integration simplifies the code and makes building robust Flex and Java applications much easier. We also configured logging to be sure that we can capture those important application activities in a log file. Setting up the logging for BlazeDS events was truly a simple task using both log4j and the Spring BlazeDS Integration framework logger class. BlazeDS also enables the gathering of performance measurements on a channel service. With that we were able to pump performance stats on message size and time to our log file. In the next chapter we'll take the next step in evolving our Flex and Java communications and take advantage of real time messaging between the two using Flex Messaging over BlazeDS to communicate with the Java messaging service, more widely known as “JMS”!
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
123
6 Flex Messaging
This introduction covers Setting up BlazeDS for messaging
Using the Flex messaging API
The Flex messaging API, bundled with BlazeDS, provides asynchronous messaging. The BlazeDS MessageService allows messaging between Flex clients, two-way POJO to subcribed Flex client communication, and JMS integration. In general, we want to use messaging for the purpose of notifying the client of changes. This will fire off an event to refresh the Flex-Bugs issues list. Figure 6.1 demonstrates this use of messaging.
Figure 6.1 Simple Polling
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
124
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
As you can see from figure 6.1 simple polling happens when the Flex client polls the server-side for a message and the server acknowledges that request with a return message. There's more to it than this but this provides the big picture. Essentially what happens is that a user would add a new issue to the system and when doing so the issue service will produce a message notification for the client to pick up. Since, the client is a consumer of a message destination that the issue service produced a message for it will get an acknowledgement that there were changes and fire a message handler event to refresh the issue list. The message handler is simply wired to the same MVP plumbing in the model with no interference to the way the rest of the client works. This chapter will exploit the use of the Flex messaging API and simple polling to receive updates from the server when changes in the model have occurred. Changes will cause an event to be dispatched that will refresh the master view and ultimately the list of issues. In the details of how messaging works depends on your needs and what style of underlying messaging architecture is chosen to implement. For example, client-to-client, JMS, Flex to POJO or Java Bean messaging. On top of that it's possible to configure the client to perform simple or long polling or even streaming. All these different usages for messaging are useful and also have benefits and consequences to go with them. It's always best to start with the simple approach first before getting complicated.
6.1 Setting up BlazeDS for Messaging There is very little configuration in order to set up a simple messaging architecture that would allow a Flex client to subscribe to a server-side component. It's also good to know that the messaging API is agnostic to the underlying messaging architecture. This makes it easy to start with a simple server-side, say with POJOs, and expand to something like JMS only if necessary.
6.1.1 Modify the services-config.xml The first thing to do is to modify the services-config.xml file. If working in the Flex-Bugs sample application it is found in the blaze-config module. The services-config.xml file is located directory /flex-bugs-blaze-config/src/main/resources. We want to prepare the server-side with a new channel definition for polling as seen in Listing 6.1. For brevity we've excluded elements of the file that have already been discussed and are not going to change.
Listing 6.1 Adding the polling channel-definition ... ... #2 true #3 4 #4 ... 1 Channel-definition declaration 2 Endpoint url 3 Polling IS enabled 4 Polling interval
The channel definition is easy to set up in that all we have to do is specify an id #1, enpoint #2, and enable polling #3. The channel definition "my-polling-amf" #1, along with the class, will be used by the Flex client for contacting the server. The enpoint element #2 provides a URL, that must be unique from other endpoints, and the endpoint class for the server. The endpoint is used by the channel service to do it's business in regards to client-side/server-side communication. Lastly, properties defining the channel are nested in a properties element. The channel definition contains behaviors that allow it to be configured in a variety of ways. In our example, we have simple polling enabled #3 and the polling interval #4 set to poll at every 4 seconds. Simple polling is generally less efficient than long polling because it continues to ping the server at each specified interval and receives acknowledgements even when there are no changes and the acknowledgements are empty. On the other hand, long polling allows the client to ping the server, the server keeps the request and returns an acknowledgement when there is a message.
Polling performance When selecting a polling mechanism consider which solution would be the least amount of overhead. Both simple and long polling can be server-side intensive if there are lots of messages being passed back and forth and lots of users. Even though long polling is generally more efficient than simple it's possible that an application with many users with frequent changes could cause more client/server friction and be less performant than controlling polling with the simple approach. If long polling is an option, streaming should be a consideration as well. Streaming is very similiar but keeps a connection open instead of opening and closing one between each transmission.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
126
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
The changes to the services-config.xml were the only changes necessary for the configuration module for adding a polling channel. Now lets move on to the webapp module changes.
6.1.2 Updating the webapp server-side module Now that we've tackled the channel definition in the services-config.xml file we can move on to modifying the webapp server-side. We'll start with the flex-spring-servlet.xml and applicationContext.xml configuration edits and then move onto to changes in the Java code. MODIFY THE FLEX-SPRING-SERVLET.XML We start by opening up the flex-spring-servlet.xml for editing that is found in the /flexbugs-web/src/main/webapp/WEB-INF directory. Here we will be adding a new Springmanaged MessageBroker, MessageService, and MessageDestination for our new polling channel. Listing 6.2 displays the edits made to the flex-spring-servlet.xml file.
Listing 6.2 Adding the MessageService and MessageBroker
#1
#2
1 Added the message-service 2 Added the message-destination
In listing 6.2 we simply added the Spring message-service #1 that points to the mypolling-amf channel we defined in the services-config.xml. Since we are using a Springmanaged MessageBroker we can simply specify the message destination #2 by adding the element and giving it the id we want to refer to in the server-side implementation. We will use the message destination to send messages from the server-side objects.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
127
MODIFY THE APPLICATIONCONTEXT.XML The last bit of configuration detail is found in the Spring applicationContext.xml. This file is also located in the WEB-INF. Listing 6.3 displays the changes to the applicationContext.xml.
Listing 6.3 Adding the Spring MessageTemplate
#1
#2 1 The Spring MessageTemplate bean 2 Injecting the MessagTemplate into the issueService
In order to push messages to the message destination from our Java objects, Spring has given us the MessageTemplate helper class #1. We simply inject an instance of the MessageTemplate into the issueService #2 and now we can use it in the IssueManagerImpl. Since the MessageTemplate is configured as a Spring bean it autodetects the MessageBroker. Otherwise, it would need to be configured. MODIFY THE ISSUEMANAGERIMPL Now that we have the Spring configuration squared away we can now take advantage of the injected MessageTemplate object by adding it to the IssueManagerImpl.java class as seen in listing 6.4.
Listing 6.4 Adding the injected MessageTemplate ... import org.springframework.flex.messaging.MessageTemplate; @WebService(serviceName = "IssueService", endpointInterface = "org.foj.service.IssueManager") public class IssueManagerImpl implements IssueManager { private GenericDao issueDao; private CommentManager commentManager; private MessageTemplate messageTemplate; public IssueManagerImpl(GenericDao issueDao, CommentManager commentManager, MessageTemplate messageTemplate) { this.issueDao = issueDao; this.commentManager = commentManager; this.messageTemplate = messageTemplate; }
#1
#2
#3
... ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
128
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
public Issue save(Issue issue) { String messageBody = "Issue was saved"; messageTemplate.send("flexMessage", messageBody); return issueDao.save(issue);
#4
}
public void remove(Long id) { commentManager.deleteAllCommentsForIssueId(id); String messageBody = "Issue was removed"; messageTemplate.send("flexMessage", messageBody); issueDao.remove(id);
#5
} } 1 Add the private field messageTemplate 2 Add the injected messageTemplate to the constructor 3 Set the messageTemplate 4 Use the flexMessage message destination on save 5 Use the flexMessage message destination on remove
In order to take use the Spring MessageTemplate first needed to create a private field to store the MessageTemplate instance #1 that we inject. Since we are injecting through a constructor object #2 we set the private field #3 to equal the injected instance. Injection could have also been performed by providing a setter method instead of the constructor approach. Either approach will work. In our example we will use the messageTemplate to send the client a text message notification. This message could be used, could also be an object, and will let the client know that there is a need to refresh. We wire up the notification to the save #4 and remove #5 methods and simply pass to the client that the issue was either saved or removed. Now that all the necessary changes are in place for simple messaging with Spring and Flex it's time to move on and update the Flex client.
6.2 Modifying the client for messaging The Flex client changes are extremely simple. The use of the MVP design pattern really isolates the changes to only two locations. We simply add the new polling channel to the ChannelSetFactory object and then finally modify the IssueModel object to receive the issue model updates from the polling channel.
6.2.1 Modify the ChannelSetFactory The first thing changed required for the client is to modify the ChannelSetFactory located in the flex-bugs-lib module in the /flex-bugs-lib/src/main/flex/org/foj/model directory. The addition of a ChannelSet configuration that communicates with the polling channel we created is needed. This will allow the Flex client to that AMFChannel and MessageBroker. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
129
Code listing 6.5 displays the changes necessary for attaching to the AMF polling channel from a Flex client.
Listing 6.5 The ChannelSetFactory package org.foj.model { import mx.messaging.ChannelSet; import mx.messaging.channels.AMFChannel; public class ChannelSetFactory { private static var _defaultChannelSet:ChannelSet; private static var _messagingChannelSet:ChannelSet;
#1
public function ChannelSetFactory() { } public static function getDefaultChannel():ChannelSet { if (_defaultChannelSet == null) { var channel:AMFChannel = new AMFChannel("my-amf", "http://localhost:8080/flexbugs/messagebroker/amf"); _defaultChannelSet = new ChannelSet(); _defaultChannelSet.addChannel(channel); } return _defaultChannelSet; } public static function getMessagingChannel():ChannelSet { if (_messagingChannelSet == null) { var pollingChannel:AMFChannel = new AMFChannel("my-polling-amf", "http://localhost:8080/flexbugs/messagebroker/amfpolling"); _messagingChannelSet = new ChannelSet(); _messagingChannelSet.addChannel(pollingChannel); }
#2
return _messagingChannelSet; } } } 1 Private field to hold the new polling ChannelSet 2 Static method to return an instance of the polling ChannelSet
The configuration for the polling ChannelSet is identical to the non-polling one except for the ID of "my-polling-amf" used when instantiating the new AMFChannel. We start by adding a private field called _messagingChannelSet #1 to hold the instance of the AMFChannel object. Next, a new static method #2 was added in order to allow any object to get an instance of the polling ChannelSet.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
130
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
6.2.2 Modify the IssueModel The final task to perform, in our sample messaging design, is to actually fire off an event when the client receives a push notification message and should refresh the issue master view DataGrid. We do this by adding a Consumer of the messaging channel. Code listing 6.6 displays the updated IssueModel object.
Messaging with good design Using the MVP design pattern makes integration with server-side messaging a breeze in the Flex client. Changes are not necessary in the Flex client MXML files or other view specific classes. Good design patterns really begin to shine after you get rolling on a project and start making changes or additions like a message architecture.
Listing 6.6 The IssueModel message Consumer package org.foj.model { ... import mx.messaging.events.MessageEvent; #1 public class IssueModel { private var _issueService:RemoteObject; public function IssueModel() { var defaultChannelSet:ChannelSet = ChannelSetFactory.getDefaultChannel(); _issueService = new RemoteObject(); _issueService.destination = "issueService"; _issueService.channelSet = defaultChannelSet; var messagingChannelSet:ChannelSet = ChannelSetFactory.getMessagingChannel();
#2
var consumer:Consumer = new Consumer(); #3 consumer.destination = "flexMessage"; #3 consumer.channelSet = messagingChannelSet; #3 consumer.addEventListener(MessageEvent.MESSAGE, messageHandler); #3 consumer.subscribe(); #3 } private function messageHandler(event:MessageEvent):void{ ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
131
#4 var eventDispatcher:EventDispatcher = EventDispatcherFactory.getEventDispatcher(); var refreshEvent:UIEvent = new UIEvent(UIEvent.REFRESH_ISSUES_BUTTON_CLICKED); eventDispatcher.dispatchEvent(refreshEvent); } ... } } 1 Import the Flex MessageEvent class 2 Get the messaging ChannelSet from the factory 3 Setting up the Flex Consumer 4 The messagehandler used to dispatch a refreshEvent
In the IssueModel we are able to receive notification of changes through the built-in Flex Messaging API. After importing the MessageEvent class #1 we need to get an instance of the messaging AMF ChannelSet from the ChannelSetfactory #2. Next we need to create a Flex Consumer #3 and establish it's properties. We configured the consumer's destination to be the "flexMessage" destination, defined in the flex-springservlet.xml.
The
consumer's
ChannelSet
was
set
to
the
one
we
got
from
the
ChannelSetFactory. A very important piece to all this is the event listener. This will allow us to invoke a method when a consumer receives a message from the server. The only thing we had to do here is say that we wanted the event to be the MessageEvent type "Message" and the method to call is "messageHandler" #4. Finally, we subscribe to the server-side message destination by calling consumer.subscribe(). Now that our Flex client is set up to subscribed to a messaging destination we are able to see it in action. This is possible by opening up two different browser instances, make changes in one while keeping an eye on the other like in figure 6.2 where the browser snippet on the left shows the issue form that persisted the issue and the browser on the right automatically getting the update.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
132
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 6.2 Trying out the messaging using two browsers
Changes should be noticable in the Issues DataGrid as changes occur in the application to the issues. The changes will occur within every 4 seconds, as specified in the channel definition. That's all there is to setting up a simple messaging solution with Flex!
6.3 Summary In this chapter we configured BlazeDS, Spring, and a Flex client for a simple messaging architecture. We changed certain Java service methods so that they would notify the client of changes. and made changes to the Flex client's IssueModel to listen as a consumer. Using the Flex messaging API we used a Consumer to subscribe to the messaging channel service and received dynamic updates for the Issues master view. We will take messaging a step farther by configuring JMS when discussing Flex with Grails. In the next chapter we will take a peek at securing and personalizing an application. The Spring security framework will be used along with its annotation to provide great flexibility with the least amount of complexity.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
133
7 Securing and personalizing your application
In this chapter:
Authentication
Authorization
Personalization
Over the past couple of chapters we've highlighted some of the great integration features that BlazeDS gives us.
Now we're going to take our integration one step further as we
strengthen our application and add some security features to it. We'll leverage the existing security infrastructure provided to us by AppFuse and not spend a lot of time going over the particulars of setting up an LDAP server, authenticating against Active Directory, or creating Access Control Lists (ACLs).
There are plenty of
resources available on the web covering these advanced topics and are beyond the scope of what we are trying to accomplish in this chapter. However, the information we cover in this chapter should be sufficient for about 90% of the applications that you'll encounter. We'll take an iterative approach to adding security to our sample application. We'll start by adding simple login and logout functionality, allowing our application to authenticate using the same mechanism that AppFuse uses internally. Following that we'll build upon that and add some security constraints to the services and lock down the destructive method calls to only users belonging to specific roles. Lastly we'll take a look at an oft-overlooked aspect of security, personalization. Before we get started however let's take a moment to cover some of the basic concepts of the Spring Security framework.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
134
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
7.1 Authentication The simplest form of security that we can add to our application is to allow our users to enter their user name and password and authenticate using our server-side. applications this is usually sufficient.
For many
There are many strategies that you can use for
authenticating your users, from rolling your own authentication methods and exposing them as remote services for your application to utilize, to implementing basic authentication measures using your web server, or in our case using a security framework like Spring Security. AppFuse provides us with a simple authentication mechanism based on user information being stored in the database out of the box, so we don't need to go through the trouble of setting up an authentication provider. The big advantage to leveraging a framework like Spring Security is that you can change out the mechanism for authentication from a database table to an LDAP server without having to change anything in your Flex application. So now let's get started with implementing the components we need to in order to allow our users to authenticate using our application.
7.1.1 Creating a ChannelSetFactory Even though all of the remote components that you use to communicate with external services have methods supporting sending credentials for authentication, this is not the preferred method of authentication.
However this is not an ideal solution because the
application would need to hold onto the username and password, in order for you manually pass it along with every single remote method call you made. Not only is this very tedious, but it's also error prone.
Instead we'll leverage the ChannelSet to authenticate us to the
server-side and maintain a session like state for us, until we either close the application or logout.
Channels and ChannelSets Flex has many different methods of communicating with BlazeDS on the server-side depending
on
the
type
of
communication:
StreamingAMFChannel, and StreamingHTTPChannel.
AMFChannel,
HTTPChannel,
These channels encapsulate the
behavior of connecting and maintaining communications with the BlazeDS components on the server-side.
You may recall eariler when setting up BlazeDS you configured an
AMFChannel in the services-config.xml file giving us a means of connecting to BlazeDS using our RemoteObject components.
The Flex remoting components such as
RemoteObject allows you to assign a set of these channels to them to use, giving you fallback and failover behavior out of the box, as well as allowing us to authenticate to the server-side.
Similar to the approach we took with the EventFactory earlier, we want to be able to create a singleton instance of a ChannelSet and be able to retrieve it so that all of our ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
135
components are utilizing the same ChannelSet. To do this we'll create a ChannelSetFactory as shown in Listing 7.1
Listing 7.1 ChannelSetFactory package org.foj.model { import mx.messaging.ChannelSet; import mx.messaging.channels.AMFChannel; public class ChannelSetFactory { private static var _defaultChannelSet:ChannelSet;
#1
public function ChannelSetFactory() { } public static function getDefaultChannel():ChannelSet { if (_defaultChannelSet == null) { var channel:AMFChannel = new AMFChannel("my-amf", "http://localhost:8080/flexbugs/messagebroker/amf"); _defaultChannelSet = new ChannelSet(); _defaultChannelSet.addChannel(channel); }
#2
#3
return _defaultChannelSet; } } } 1 The singleton instance 2 Static factory method 3 The AMF Channel
The code for the ChannelSetFactory looks very similar to the EventDispatcherFactory. We start out by declaring our private instance variable to hold our ChannelSet (#1). Then we define a static factory method that the rest of the application will use to retrieve the ChannelSet (#2). Inside this method we check to see if the instance has been instantiated yet, and if not create one and then assign our AMFChannel to it (#3). Then all that's left is to return the ChannelSet to the caller. Next let's create our custom event class for our login panel to utilize.
7.1.2 Creating a UserEvent The UserEvent class should look familiar; it's just another custom event class just like the last one that we created earlier for the rest of the events that our application uses. We're creating this one separate from the other event class, as it is a separate area of concern from UIEvents. This also will make it easier to later extract the login panel to a separate library later, by keeping all of the login functionality separate from the rest of the application logic.
Listing 7.2 UserEvent ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
136
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
package org.foj.event { import flash.events.Event; public class UserEvent extends Event{ public static var LOGIN_BUTTON_PRESSED:String = "loginButtonPressed"; public static var LOGOUT_BUTTON_PRESSED:String = "logoutButtonPressed"; public static const USER_LOGGED_IN:String = "userLoggedIn"; public static const CURRENT_USER_UPDATED:String = "currentUserUpdated";
#1
public var data : *;
#2
public function UserEvent(type : String, bubbles : Boolean = true, cancelable : Boolean = false) { super(type, bubbles, cancelable); }
#3
#4
} } 1. 2. 3. 4.
Event type constants Data field Overloaded constructor Call to super
Listing 7.2 shows our custom UserEvent. We start out just like we did in our last event class by defining some constants to describe what events we'll be utilizing this event class for (#1).
Next we define a data member variable to carry along any data with the event if
necessary (#2). The next thing to do is to declare an overloaded constructor (#3) and inside of this is simply a call to the base constructor in the Event class (#4). Now that we've got our custom event defined, let's put it to use and start to create the login panel.
7.1.3 Creating a Login panel So when deciding how to implement the login functionality there were many possibilities. Whether or not to create a separate login screen, a popup, or implement it inline.
Since
we're not going to attempt to prevent our users from accessing and browsing our application unless they're logged in, having a separate login screen that takes our users away from the application doesn't make much sense. Neither does blocking the application with a modal dialog forcing them to login. So we're going with the simplest possible solution, which is to present the user with a couple of fields in the upper right hand side of the application to enter their username and password into.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
137
Figure 7.1 Logging in to the application
The login panel will utilize a view stack just like our main application, so that as the users login, the look of the login panel will change to reflect that the user has logged in successfully as shown in Figure 7.1. Then when the user logs out, the view will change back to the initial logged out state.
Listing 7.3 LoginPanel.mxml
#1
#2
138
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
private function logout():void { var logoutEvent:UserEvent = new UserEvent(UserEvent.LOGOUT_BUTTON_PRESSED); EventDispatcherFactory.getEventDispatcher() .dispatchEvent(logoutEvent); }
#6
]]>
#8
#9
1. 2. 3. 4. 5. 6. 7. 8. 9.
Extends Group Set the layout for the component The presenter for the login component Init method Event handler for the login button Event handler for the logout button The view stack The loggedOut view The loggedIn view
Listing 7.3 shows the code for our LoginPanel. Like most of our other components this panel will extend the Group component (#1) and we define its layout to be horizontal (#2). Next we define a private member variable for our presenter (#3) and initialize it in our init method (#4) passing in a reference to our panel in the constructor. Then we define a couple of event handlers for the login button being clicked (#5) as well as the logout button being clicked (#6). These two event handlers follow the same pattern that we established back in chapter 3. The simply create the appropriate UserEvent and dispatch them for our Presenter and anyone else who is concerned can react. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
139
Next we define our visual components starting with defining a ViewStack (#7) that will allow us to switch between the two different states our login panel can be in. Inside of the ViewStack component we create two HBox components, one for our loggedOut view state (#8) and one for the loggedIn view state (#9). Now we need to create our presenter for the LoginPanel.
7.1.4 Creating a Login Presenter The LoginPresenter class shown in Listing 7.4 actually starts out pretty simple. It follows the same general pattern that all of our previous presenters have, only having to react to a couple of button presses and doesn't have to maintain any kind of state just yet.
Listing 7.4 LoginPresenter.as package org.foj.presenter { ... public class LoginPresenter { private var _view:LoginPanel; private var _model:LoginModel;
#1 #2
public function LoginPresenter(view:LoginPanel) { this._view = view; this._model = new LoginModel(); EventDispatcherFactory.getEventDispatcher() .addEventListener(UserEvent.LOGIN_BUTTON_PRESSED, login); EventDispatcherFactory.getEventDispatcher() .addEventListener(UserEvent.LOGOUT_BUTTON_PRESSED, logout);
#3
#4
} private function login(event:UserEvent = null):void { var responder:IResponder = new AsyncResponder( loginResult, handleError); _model.login(_view.usernameInput.text, _view.passwordInput.text, responder); } private function logout(event:UserEvent = null):void { var responder:IResponder = new AsyncResponder( logoutResult, handleError);
#5
#6
_model.logout(responder); } private function loginResult(event:ResultEvent, token:Object = null):void { _view.loginStack.selectedChild = _view.loggedIn; _view.passwordInput.text = "";
#7
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
140
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Alert.show("You have logged in as: " + event.result.name); } private function logoutResult(event:ResultEvent, token:Object = null):void { _view.loginStack.selectedChild = _view.loggedOut; Alert.show("You have successfully logged out"); }
#8
private function handleError(event:FaultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); Alert.show(event.fault.faultString); }
#9
} } 1. 2. 3. 4. 5. 6. 7. 8. 9.
The view The model Add event listener for login Add event listener for logout Event handler for login event Event handler for logout event Event handler for login result Event handler for logout result Error handler
We start out our presenter by defining our view (#1) and model (#2) instance variables. Then in our constructor we assign the view reference passed in to our instance variable and new
up
and
instance
of
our
model.
Then
we
add
and
event
listener
for
LOGIN_BUTTON_PRESSED event (#3), assigning the login handler (#5), and an event listener for the LOGOUT_BUTTON_PRESSED event (#4), assigning the logout handler (#6) to it. Inside the login handler, we create an AsyncResponder to pass into our model, pointing it to the loginResult handler method (#7) and the error handler (#9). Then we call the login method on our model passing in the username, password and the responder.
Inside the
logout handler, we similarly create a responder pointing to the logoutResult (#8) and error handler methods, then call the logout method on our model passing in the responder. When the login method from the model responds, our login result handler gets invoked, and switches the current view state of our login panel for us, blanks out the password box contents so that the user will be forced to type in a password the next time they try to login, and finally displays an Alert stating that they have logged in successfully. On the other side of the equation, when the logout method from the model responds, the logout result handler gets called, switches the view stack back to the initial logged out state, and alerts the user that they have logged out.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
141
7.1.5 Creating a Login manager Now that we have the presenter created we need to implement our model so that we can actually authenticate our users against our application. Listing 7.5 shows our LoginModel for the LoginPanel.
Listing 7.5 LoginModel.as package org.foj.model { import mx.messaging.ChannelSet; import mx.rpc.AsyncToken; import mx.rpc.IResponder; import mx.rpc.remoting.RemoteObject; public class LoginModel { private var _defaultChannelSet:ChannelSet; public function LoginModel() { _defaultChannelSet = ChannelSetFactory.getDefaultChannel(); } public function login(username:String, password:String, responder:IResponder):void {
#1
#2
if (!_defaultChannelSet.authenticated) { var token:AsyncToken = _defaultChannelSet.login(username, password); token.addResponder(responder); } } public function logout(responder:IResponder):void { var token:AsyncToken = _defaultChannelSet.logout(); token.addResponder(responder); }
#3
} } 1. Get the default ChannelSet 2. Login method 3. Logout method
The model for the LoginPanel is very simple to start with. Inside the constructor for our model, we get the default ChannelSet from our ChannelSetFactory (#1), and assign it to an instance variable.
Inside of the login method (#2), we first check to see if the user is
already logged in, as this sometimes results in an error condition and BlazeDS will complain about the ChannelSet being authenticated already. If the user is not logged in currently, it will then call the login method on the ChannelSet.
The logout method (#3) simply calls
logout on the ChannelSet, not having to worry about checking to see if the user is currently logged in or not.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
142
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
7.1.6 Updating the Header Now we need to update our Header.mxml to include our newly created LoginPanel. Listing 7.6 shows the changes to our Header.mxml to include the newly created LoginPanel.
Listing 7.6 Header.mxml
#1
1 The LoginPanel
The changes necessary to add the Login Panel to the Header are very trivial.
We've
made a few minor tweaks to the layout and spacing components and only had to add one line of XML to add our LoginPanel to the header.
Once that is done, all that's left is to
configure BlazeDS to enable security.
7.1.7 Enabling security for Flex In order to enable security for BlazeDS, we need to make a small modification to our flexspring-servlet.xml context file. Listing 7.7 shows this change. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
143
Listing 7.7 flex-spring-servlet.xml With all of that in place, now it's time to run the application and try to login to our application.
There are two users created by default in AppFuse, a regular user with the
ROLE_USER authority and an administrator who has the ROLE_ADMIN authority. The regular user's username is "user" and a password of "user", and in case you hadn't guessed, the administrator's username is "admin" with a password of "admin".
Figure 7.2 The login panel in action
After you build and run the application you should be presented with something that looks like Figure 7.2. Try to log in with the user/user account and you'll be presented with an Alert window telling you that you've successfully logged in. If you mistype the password, you should be presented with an error message stating that your credentials were incorrect. This was just the first piece of functionality we wanted to implement in this chapter, now let's move on to adding authorization to our application.
7.2 Authorization Sometimes simply authenticating your users isn't enough.
If you want to be able to
selectively secure specific parts of your application and not others, for example allowing users who are not authenticated to your application to be able to browse the list of open ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
144
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
bugs and comments, but restrict logging new bugs to authenticated users, then this is where authorization comes in to play. Many application frameworks do not have this level of support, so you end up having to put crosscutting concerns such as authorization throughout your code, surrounding certain functionality with checks to see if your user is authenticated or has permissions to access that particular piece of functionality. However, this is one area where the Spring Security framework shines. Spring Security leverages Aspect Oriented Programming (AOP) to allow you to apply very complex rules about who should be able to execute what method calls. We can even get such fine-grained control that we can allow logged in users to add new issues and comments and make changes to existing issues and comments, but restrict deleting anything to only those who are administrators. Unfortunately we won't be discussing much about AOP in this book, if you'd like to learn more about how to use AOP with Spring, check out Craig Walls' excellent book Spring in Action (http://manning.com/walls4/).
7.2.1 Flex Spring Security primer The Spring-Flex integration provides us with a few different methods of securing our application. We'll start off with the broadest control and work our way to more and more fine-grained control. The first method we could use is to secure an entire AMF channel using the flex-spring-servlet.xml as shown below, but this would not be desirable for the fact that we lose all ability to define different levels of security for different parts of our application. The next method available to us is to secure our application by adding security constraints directly to your destinations by configuring your destination in a remoting-config.xml like shown below. ... Custom ROLE_USER This would apply a security constraint for the issueService, on a system wide level, allowing only users in the role ROLE_USER access to this destination.
This method of
securing an application, while simple, will not work for what we're trying to do with our application for a couple of reasons. This would effectively cut off any ability to do method level authorization for our application, and also we are not defining our destinations using a ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
145
remoting-config.xml, we're allowing the Spring-Flex to dynamically configure our remoting destinations for us. Moving on to a little more fine-grained security, you can setup a global method interceptor using AOP pointcut syntax to define a pattern of methods you want to secure. The following code shows an example of this taken from AppFuse. This will constrain any call to a method named getUsers, contained in a class or interface named UserManager whose package name ends with "service". As you can see this method allows you to apply very complex patterns to define which methods get secured. The code above shows how you can secure methods at the bean level in XML by using the Spring application context. This gives you more localized control over your security than the global method illustrated earlier, keeping the security constraints a little closer to the code that it affects. public interface IssueManager { ... @Secured({"ROLE_USER", "ROLE_ADMIN"}) @RemotingInclude Issue save(Issue issue); @Secured({"ROLE_ADMIN"}) @RemotingInclude void remove(Long id); } The above code shows the final method of securing your business methods by using the @Secured annotation. This is the most localized method of security and the method we're going to use in our examples in little while. This keeps the declared security constraints right with the code you're trying to secure. The other advantage to using the annotations over the other methods is that if someone decides they want to override what you've defined in the code, they can override it using the XML configuration.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
146
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
7.2.2 Spring Integration Security In order for our Flex application to be able to take advantage of using Spring Security for authorization, we first need to add the Spring Integration Security dependency to our pom.xml in the flex-bugs-web project as shown in Listing 7.8.
Listing 7.8 Spring Integration Security dependency org.springframework.integration spring-integration-security 1.0.3.RELEASE By adding this dependency, BlazeDS will use a special LoginCommand object on the server side that enables the ChannelSet login and logout functionality to integrate with Spring Security's authorization mechanisms by returning the username and any authorities that user has in the result event. It also does some error translating on the server side to translate any security exceptions that may happen into their BlazeDS security exception equivalent so that the error can be reported back to the Flex client instead of returning a HTTP 403 status code which will completely and utterly break your application. You can read more about the Spring Integration Security in the docs for the Spring-Flex integration at http://static.springsource.org/spring-flex/docs/1.0.x/reference/html/.
7.2.3 @Security annotations There are many methods you can use to secure your application; we have chosen to continue along the path of using annotations where possible to declare which interface methods we want to secure and what security constraints we want to place on them. We'll start out by adding the necessary annotations to the IssueManager, as shown in Listing 7.9.
Listing 7.9 IssueManger.java package org.foj.service; import import import import
org.foj.model.Issue; org.springframework.flex.remoting.RemotingDestination; org.springframework.flex.remoting.RemotingInclude; org.springframework.security.annotation.Secured;
import javax.jws.WebService; @WebService @RemotingDestination(channels = {"my-amf"}) public interface IssueManager { @RemotingInclude java.util.List getAll(); @RemotingInclude Issue get(Long id);
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
147
@Secured({"ROLE_USER", "ROLE_ADMIN"}) @RemotingInclude Issue save(Issue issue);
#1
@Secured({"ROLE_ADMIN"}) @RemotingInclude void remove(Long id);
#2
} 1. save method annotation 2. remove method annotation
As you can see we've added a @Secured annotation to the save method (#1) and declared that only users that have the ROLE_USER and ROLE_ADMIN can add and update issues.
We defined both roles since our roles are not overlapping, meaning that
ROLE_ADMIN is not a superset of ROLE_USER but rather a separate role altogether. Next we declare the remove method to be only usable by users that have the ROLE_ADMIN granted to them (#2). Next we add the necessary annotations to the CommentManager as shown in Listing 7.10.
Listing 7.10 CommentManager.java package org.foj.service; import import import import
org.foj.model.Comment; org.springframework.flex.remoting.RemotingDestination; org.springframework.flex.remoting.RemotingInclude; org.springframework.security.annotation.Secured;
import javax.jws.WebService; import java.util.List; @WebService @RemotingDestination(channels = {"my-amf"}) public interface CommentManager { @RemotingInclude List findCommentsByIssueId(Long issueId); @Secured({"ROLE_ADMIN"}) @RemotingInclude void deleteAllCommentsForIssueId(Long issueId);
#1
@RemotingInclude Comment get(Long id); @Secured({"ROLE_USER", "ROLE_ADMIN"}) @RemotingInclude Comment save(Comment comment);
#2
@Secured({"ROLE_ADMIN"}) @RemotingInclude void remove(Long id);
#3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
148
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
} 1. deleteAllCommentsForIssueId method annotation 2. save method annotation 3. remove method annotation
Similar to the IssueManager annotations, we start out by declaring only members of ROLE_ADMIN can call the deleteAllCommentsForIssueId (#1) and remove (#3) methods. Then we declare that the save method can be called by users with either ROLE_USER or ROLE_ADMIN privileges (#2). Notice that we never specified any security constraints for the get methods of our services.
This ensures that anyone can call them whether they're
authenticated to the application or not.
Now that we've declared our security with
annotations, we need to enable support for these annotations in Spring Security.
7.2.4 Overriding the default security settings AppFuse has Spring Security installed and configured out of the box, so in order to extend upon that we need to extract out their security.xml configuration and put that in our flexbugs-web project in the WEB-INF folder alongside our other Spring configuration files. To do this we copy the security.xml that is included in the appfuse-web-common.war that basically gets overlaid on top of the flex-bugs-web.war when you build this project.
Listing 7.11 security.xml
#1
#2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
149
#3 1. Security by url pattern 2. The authentication provider 3. Enable security annotations
Listing 7.11 shows the modified security.xml, which for the most part is identical to the security.xml that is included in AppFuse.
The first section (#1), which is configured by
AppFuse for us, dictates which url patterns to intercept and pass through the security filters. We don't need to specify anything in this section for our Flex application in here as the Spring Integration Security handles all the filtering for our Flex/BlazeDS interactions.
The
second section (#2) defines our authentication provider, which in this case is just using the default userDao provided by AppFuse.
If you wanted to change that method of
authentication to an LDAP server for instance, this is where you would configure that. The last section (#3) is where we make our minor modification. We added a couple of attributes to the global-method-security tag to set the secured-annotations to "enabled" and the jsr250-annotations to "enabled" as well.
7.2.5 Updating IssueModel and CommentModel Now all that's left for us to do to enable authorization to work in our Flex application is to modify our models to use the ChannelSetFactory we created earlier. Listing 7.12 shows the changes for the IssueModel.
Listing 7.12 IssueModel.as package org.foj.model { ... public class IssueModel { private var _issueService:RemoteObject; public function IssueModel() { var defaultChannelSet:ChannelSet = ChannelSetFactory.getDefaultChannel(); _issueService = new RemoteObject(); _issueService.destination = "issueService"; _issueService.channelSet = defaultChannelSet;
#1
#2
} ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
150
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
... } } 1. Get the ChannelSet 2. Set the channelSet on the service
As you can see the impact of adding this functionality to our Flex application is quite minimal once again. First we get the default ChannelSet from the ChannelSetFactory (#1), just like we did earlier for our LoginModel, and we set the channelSet property on our RemoteObject (#2) to our defaultChannelSet. Listing 7.13 shows the same changes for our CommentModel.
Listing 7.13 CommentModel.as package org.foj.model { ... public class CommentModel { private var _commentService:RemoteObject; public function CommentModel() { var defaultChannelSet:ChannelSet = ChannelSetFactory.getDefaultChannel(); _commentService = new RemoteObject(); _commentService.destination = "commentService"; _commentService.channelSet = defaultChannelSet;
#1
#2
} ... } } 1. Get the ChannelSet 2. Set the channelSet on the service
Now you can build and run the application and if you try to save or delete an item without being logged in with the proper authorities granted, you will be presented with an error message like what's shown in Figure 7.3
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
151
Figure 7.3 Error when trying to execute method without authorization
Again, we went rather simple with our error handling, merely displaying the fault message in an Alert box, however you'll notice that the error message does describe very well what the error is. The last enhancement we're going to make to our application with respect to security is to add some personalization.
7.3 Personalization When you think about leveraging a security framework in your Flex application, you probably didn't think about personalization, even though the two are very much related. Recall earlier when we discussed the Spring Integration Security module, that one of the things that are returned in the ResultEvent is the username of the person who was authenticating. Now that your user has authenticated to our application, you can now use the information returned from the login process to get more information such as the user's first and last name.
Why not just use the username that the user typed into the login box you ask?
Simple, if we wait for the login method to return successfully and take the value returned instead, we are guaranteed that the user has first authenticated, and secondly that the username returned in the ResultEvent is the correct username for that user.
7.3.1 Adding the UserService to the LoginModel Since the ResultEvent returned from logging in and out of the ChannelSet only includes the user name, we'll have to use some other method to get the user's full name from the application. Fortunately AppFuse exposes this functionality in the userDao object, however, we do still have to tell BlazeDS to expose this to our Flex application. So we need to add the following line to our flex-spring-servlet.xml in the WEB-INF folder: This one line of xml configuration accomplishes the same thing as you did earlier in Chapter 5 with the @RemotingDestination annotations. The difference being that we don't have to crack open the AppFuse source code to expose this Spring bean as a remote service to Flex. Now let's add this service to the Login Model. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
152
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Listing 7.14 LoginModel.as package org.foj.model { import mx.messaging.ChannelSet; import mx.rpc.AsyncToken; import mx.rpc.IResponder; import mx.rpc.remoting.RemoteObject; public class LoginModel { private var _userService:RemoteObject; private var _defaultChannelSet:ChannelSet;
#1
public function LoginModel() { _defaultChannelSet = ChannelSetFactory.getDefaultChannel(); _userService = new RemoteObject(); _userService.destination = "userDao"; _userService.channelSet = _defaultChannelSet;
#2
} ... public function getUserDetails(username:String, #3 responder:IResponder):void { var token:AsyncToken = _userService.loadUserByUsername(username); token.addResponder(responder); } } } 1. The userService 2. Creating the RemoteObject 3. getUserDetails method
Listing 7.14 shows the code that we need to add to the LoginModel.
First we need to
define an instance variable for our userService (#1) and create a new instance of it in the constructor (#2).
Just like our other remote objects, we set its destination to "userDao"
since that is what our Spring bean is defined as, and now also the channelSet to the defaultChannelSet that we got from the ChannelSetFactory.
Lastly we define a method
called getUserDetails which takes as its first argument the username to lookup, and a responder to callback to when the service returns. Now let's update the LoginPresenter to use this new functionality.
7.3.2 Updating the LoginPresenter There are a couple of minor modifications that we need to now make to the LoginPresenter to use the new method we just created in the LoginModel.
Listing 7.15 shows these
changes.
Listing 7.15 LoginPresenter.as package org.foj.presenter { ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
153
... public class LoginPresenter { private var _view:LoginPanel; private var _model:LoginModel; public function LoginPresenter(view:LoginPanel) { ... EventDispatcherFactory.getEventDispatcher() .addEventListener(UserEvent.USER_LOGGED_IN, getUserDetails);
#1
} ... private function loginResult(event:ResultEvent, token:Object = null):void { _view.loginStack.selectedChild = _view.loggedIn; _view.passwordInput.text = "";
#2
var userLoggedInEvent:UserEvent = new UserEvent(UserEvent.USER_LOGGED_IN); userLoggedInEvent.data = event.result.name; EventDispatcherFactory.getEventDispatcher() .dispatchEvent(userLoggedInEvent); } private function logoutResult(event:ResultEvent, token:Object = null):void { _view.userName.text = ""; _view.loginStack.selectedChild = _view.loggedOut; Alert.show("You have successfully logged out");
#3
var userChangedEvent:UserEvent = new UserEvent(UserEvent.CURRENT_USER_UPDATED); userChangedEvent.data = null; EventDispatcherFactory.getEventDispatcher() .dispatchEvent(userChangedEvent); } private function getUserDetails(event:UserEvent = null):void { var responder:IResponder = new AsyncResponder( getUserDetailsResult, handleError);
#4
_model.getUserDetails(event.data, responder); }
private function getUserDetailsResult(event:ResultEvent, token:Object = null):void { var user = event.result; _view.userName.text = user.fullName; Alert.show("Welcome Back " + user.fullName);
#5
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
154
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
var userChangedEvent:UserEvent = new UserEvent(UserEvent.CURRENT_USER_UPDATED); userChangedEvent.data = user; EventDispatcherFactory.getEventDispatcher() .dispatchEvent(userChangedEvent); } ... } } 1. 2. 3. 4. 5.
Event listener for user logged in Event handler for user logged in result Event handler for user logged out result Event handler for user logged in Handler for userService result
The first change we make is to add a listener for the USER_LOGGED_IN event (#1) so that we can know when we need to update the user details.
Next we make a slight
modification to the method that is called when the result comes back from the call to login on the IssueModel (#2). Here we take the username from the event result and create a new UserEvent of type USER_LOGGED_IN, and we set the event's data property to the username. This allows us to pass along the username to whoever would want to respond to a user logging in. We do something similar in the logged out result handler (#3) except we create a CURRENT_USER_UPDATED event to notify other parts of the application that the currently logged in user has been changed, and set it's data property to null. Next we define the event handler for the USER_LOGGED_IN event listener we defined in the constructor (#4). Inside this method we create an AsyncResponder object and make a call to the getUserDetails method on the IssueModel we defined in the previous section. When the result from this call comes back, it gets handled by our getUserDetailsResult method (#5) where we update the view to show the current user's full name, and we also create a new CURRENT_USER_UPDATED event setting its data property to the User object that came back in the ResultEvent.
Now let's look at updating the DetailPresenter and
CommentsListPresenter, which will be listening for the CURRENT_USER_UPDATED event that we just added.
7.3.3 Updating the DetailPresenter and CommentsListPresenter Now that our LoginPresenter is broadcasting an event that notifies the rest of the application that the currently logged in user has changed, we can add some event listeners to the other parts of the application that may want to listen for that. We'll start with the DetailPresenter so that we can default the author field for any new issues to the currently logged in user. Listing 7.16 shows the changes needed to add this to the DetailPresenter.
Listing 7.16 DetailPresenter package org.foj.presenter { ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
155
... public class DetailPresenter { private private private private
var var var var
_currentUser:User; _issue:Issue; _view:DetailView; _issueModel:IssueModel;
#1
public function DetailPresenter(view:DetailView) { ... EventDispatcherFactory.getEventDispatcher() .addEventListener(UserEvent.CURRENT_USER_UPDATED, currentUserChanged);
#2
} ... private function cancelChanges(event:UIEvent = null):void { selectedIssue = new Issue(); selectedIssue.reportedBy = _currentUser.fullName; #3 var event:UIEvent = new UIEvent(UIEvent.SELECTED_ISSUE_CHANGED); event.data = selectedIssue; EventDispatcherFactory.getEventDispatcher().dispatchEvent(event); } ... private function currentUserChanged(event:UserEvent):void { _currentUser = event.data; if (selectedIssue.id == 0) { _view.issueReportedBy.text = _currentUser == null ? "" :_currentUser.fullName; } }
#4
... } } 1. 2. 3. 4.
Instance variable for current user Event listener for current user changing Default the author field Event handler for currentUserChanged
First we start out by adding an instance variable for our DetailPresenter to be able to cache
the
current
user
in
(#1).
Next
we
add
an
event
listener
for
the
CURRENT_USER_UPDATED event (#2) and point it at the currentUserChanged event handler.
Inside the cancelChanges event handler, which gets invoked anytime the user
wants to clear out the changes made in the detail form and creates a new issue, we set the default for the reportedBy property to the current user's full name (#3).
Lastly we
implement an event listener for the current user changing (#4), where we set our instance
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
156
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
variable of current user to the user that was passed along in the event. We then also update the reported by field on the view if the currently displayed issue is not an existing issue.
Listing 7.17 CommentsListPresenter package org.foj.presenter { ... public class CommentsListPresenter { private private private private private private
var var var var var var
_currentUser:User; _selectedIssue:Issue; _selectedComment:Comment; _commentModel:CommentModel; _view:CommentsView; _pop1:EditCommentForm;
#1
public function CommentsListPresenter(view:CommentsView = null) { ... EventDispatcherFactory.getEventDispatcher() .addEventListener(UserEvent.CURRENT_USER_UPDATED, currentUserChanged);
#2
} ... private function addNewComment(event:* = null):void { selectedComment = new Comment(); selectedComment.issue = selectedIssue; _pop1 = PopUpManager.createPopUp((_view as UIComponent).root, EditCommentForm, true) as EditCommentForm; _pop1.author.text = _currentUser.fullName; PopUpManager.centerPopUp(_pop1 as UIComponent);
#3
} ... private function currentUserChanged(event:UserEvent):void { _currentUser = event.data; }
#4
... } } 1. 2. 3. 4.
Instance variable for current user Event listener for user changed Default the author field Event handler for user changed event
Listing 7.17 shows the changes for the CommentsListPresenter.
Just like the Detail
presenter, we start out by defining an instance variable to allow this presenter to cache the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
currently
logged
in
Allmon and Anderson / Flex on Java
user
(#1).
After
that,
we
add
an
event
157
listener
for
the
CURRENT_USER_UPDATED event (#2) pointing it to the currentUserChanged method (#4), where we set the current user to the user passed in with the event.
Inside the
addNewComment method we default the text of the author field in the comment popup to be the current user's full name (#3).
Figure 7.4 Add new comment defaults Author to currently logged in user
Now build and run the application. If you now login using the Tomcat User username and password and try to add a new comment to an issue, you should see that the Author field is now defaulting to the logged in user's full name as shown in Figure 7.4. When you log back out you should see that the fields are no longer being defaulted to anything.
7.4 Summary When we started this chapter, we had an application that anybody could have come in and added new issues and removed issues that may have been critical, with no sort of controls to prevent such destructive behavior. Over the course of the chapter we slowly started adding measures to control who has access to what. We started out adding the simplest of security constraints to our application, allowing our users to authenticate to our server-side. Though this doesn't do us much good until we can control what our users can do to the data. Once we added our authorization constraints, we had fine-grained control over who could modify and delete our data.
This is rather
important, because up until a few years back before the Spring Security, the most common way to declaratively define security constraints down to the method being called this way was to use EJBs and container managed security, which would probably have not made this tight of integration easy. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
158
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Even in our fairly simple example we have been able to define three different levels of security for our application and with minimal intrusion to our existing web application. The ability to declaratively define security constraints on our business methods through the use of either the @Security annotations, or even using AOP pointcut syntax is extremely powerful. This means that you no longer have to litter your source code with if statements checking if the user is logged in and whether or not they have the right level of access to perform the operation. The last thing we did in our journey through this chapter was to add some functionality that doesn't really do much for securing our application, but will likely delight our users for the simple fact that the application is personalized a little bit to them. They no longer have to type in their names in to the author fields when adding new issues and comments, and can visually see that they are logged in, and who they are logged in as. In the next chapter we're going to continue enhancing our application by adding some data visualization to put a birds-eye view on our data. We're going to tackle creating a pie chart component from scratch using the Degrafa framework.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
159
8 Charting with Degrafa
In this chapter
Introduce the Degrafa drawing library
Create a custom PieChart component
Create an ItemRenderer for a DataGrid
Dynamic object creation
Now that we've got our application communicating with our server side, and secured, it's time to add some data visualization components to our application and put a bird's eye view on our data. Adding data visualization components to your application allows people to see with a glance things like how many open issues there are for a project and what number of bugs versus the number of feature requests are, for example without having to manually count them in the data grid in the master view. There are many other ways that the data could be visualized, and we're just going to scratch the surface in this chapter. Adobe does provide some data visualization components, however the only way to get them is to purchase a license for the Professional version of their Flex Builder IDE. Since our goal here is to be able to do Flex development using only free and open source technologies, we've decided to create our own. Besides, being it's more fun that way.
8.1 Drawing in Flex Flex and Flash provide some very powerful drawing libraries that we could leverage to create our custom graph components, however instead we're going to leverage an open source graphics library called Degrafa. Using Degrafa gives us the ability to declaratively build our graphing components rather than having to deal with the complex calculations involved in drawing something like a pie chart slice as illustrated in Listing 8.1, which shows an example ActionScript
class
specifically
for
drawing
a
pie
chart
slice
found
at
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
160
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
http://www.adobe.com/devnet/flash/articles/adv_draw_methods.html.
Notice how much
trigonometry is involved in creating something as simple as a pie chart slice from scratch.
Listing 8.1 Example of drawing in Actionscript /*------------------------------------------------------------mc.drawWedge is a method for drawing pie shaped wedges. Very useful for creating charts. Special thanks to: Robert Penner, Eric Mueller and Michael Hurwicz for their contributions. -------------------------------------------------------------*/ MovieClip.prototype.drawWedge = function(x, y, startAngle, arc, radius, yRadius) { // ============== // mc.drawWedge() - by Ric Ewing (
[email protected]) - version 1.3 - 6.12.2002 // // x, y = center point of the wedge. // startAngle = starting angle in degrees. // arc = sweep of the wedge. Negative values draw clockwise. // radius = radius of wedge. If [optional] yRadius is defined, then radius is the x radius. // yRadius = [optional] y radius for wedge. // ============== // Thanks to: Robert Penner, Eric Mueller and Michael Hurwicz for their contributions. // ============== if (arguments.length360) { arc = 360; } // Flash uses 8 segments per circle, to match that, we draw in a maximum // of 45 degree segments. First we calculate how many segments are needed // for our arc. segs = Math.ceil(Math.abs(arc)/45); // Now calculate the sweep of each segment. segAngle = arc/segs; // The math requires radians rather than degrees. To convert from degrees // use the formula (degrees/180)*Math.PI to get radians. theta = -(segAngle/180)*Math.PI; // convert angle startAngle to radians angle = -(startAngle/180)*Math.PI; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
161
// draw the curve in segments no larger than 45 degrees. if (segs>0) { // draw a line from the center to the start of the curve ax = x+Math.cos(startAngle/180*Math.PI)*radius; ay = y+Math.sin(-startAngle/180*Math.PI)*yRadius; this.lineTo(ax, ay); // Loop for drawing curve segments for (var i = 0; i ... ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
166
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#1
#2
#3
#4
#5 1. A spacer to help layout the component 2. The degrafa surface 3. The geometry group containing the pie chart 4. A drop shadow for the pie chart 5. The legend for our pie chart 6. A custom ItemRenderer
Listing 8.4 shows the rest of our pie chart component. First we added a spacer (#1) to the component in order to put a little bit of padding between our pie chart and its surrounding components. Next we add a degrafa surface component (#2) and a geometry group (#3) to hold the rest of our degrafa components necessary for our pie chart component. The geometry group is the component that we'll eventually add our pie chart slices to when we create them. We've also added a drop shadow filter (#4) to the geometry group to add a little bit of visual flair to our pie chart.
Lastly we define the data grid
component (#5) for our chart legend, with a custom ItemRenderer (#6) to display the color that corresponds to the data in the chart, which we'll create in a little bit.
8.3.3 Creating the PieChartSlice Now that we've got our pie chart component defined, let's move on to defining the slices that will make up our pie chart.
Our pie chart slice is a rather simple component.
We
probably could have just created our pie chart slice programmatically in ActionScript,
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
167
however this approach allows us the ability to define some sensible defaults declaratively in MXML and add some behavior as well.
Listing 8.5 PieChartSlice.mxml
#3
1. Extends GeometryGroup 2. Our pie chart slice
Listing 8.5 shows the code for our pie chart slice.
It will extend from GeometryGroup
(#1) instead of the standard Flex Group component that we extended earlier in Chapter 2. Next we define a refresh method (#2) to abstract some behavior away from our presenter. Lastly we add an EllipticalArc component to the component (#3) and set some default values such as its width, height, and most importantly its closureType property, which we set to "pie".
8.3.4 Creating a custom ItemRenderer The next component we're going to create is the custom ItemRenderer for our pie chart legend. This simple component will draw a colored box in our data grid cell to correspond with the colors of the pie chart. The code necessary for this component is shown in Listing 8.6.
Listing 8.6 PieLegendRenderer.mxml {data.legend}"
#1 #2
#3 #4
#5
1. Extends HBox 2. Spacer to help align our box 3. SolidFill for our rectangle 4. The color of our legend item 5. The Rectangle
Our ItemRenderer is extending HBox (#1) since all ItemRenderers for the DataGrid component must be halo components. Next we add a Spacer component (#2) to help align our rectangle the way we want it. The next component we create is a SolidFill (#3), which defines the fill color we want our RegularRectangle (#5) to be. Now that we have finished creating all of our visual components for the pie chart, let's move on to creating our presenter.
There is an implicitly defined variable available to our ItemRenderer named
"data", which corresponds to the item in the dataProvider that we're rendering. We use this implicit variable to set the color of our Fill object (#4), which is contained in the legend property of our object.
8.3.5 Creating a Presenter for our PieChart Now that we have all the visual components created for our pie chart, let's create the presenter. The presenter for the pie chart becomes a little bit more involved than any of our previous presenters, but it shouldn't be too hard to follow.
Listing 8.7 shows the code
necessary for the PieChartPresenter.
Listing 8.7 PieChartPresenter package org.foj.presenter { import import import import import import import
com.degrafa.paint.SolidFill; org.foj.components.PieChart; mx.collections.ArrayCollection; org.foj.components.PieChartSlice; org.foj.event.EventDispatcherFactory; org.foj.event.PieChartEvent; org.foj.model.PieChartModel;
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
169
public class PieChartPresenter { private private private private
var var var var
_view:PieChart; _model:PieChartModel; _id:String; _dataProvider:ArrayCollection;
#1
public function PieChartPresenter(view:PieChart, id:String) { this._view = view; this._model = new PieChartModel(); this._id = id;
#2
EventDispatcherFactory.getEventDispatcher() .addEventListener(PieChartEvent.DATA_PROVIDER_UPDATED, refreshData); } public function set dataProvider(data:ArrayCollection):void { _view.legendDataGrid.dataProvider = data; this._dataProvider = data; }
#3
public function get dataProvider():ArrayCollection { return this._dataProvider; } private function refreshData(event:PieChartEvent = null):void { if (event.id == _id) { changeData(event.data); } }
#4
private function changeData(data:ArrayCollection):void { dataProvider = data; createSlices(); } private function createSlices():void { while (dataProvider.length > _view.pieGroup.numChildren) { _view.pieGroup.addChild(new PieChartSlice()); } setLegendColors(); redrawSlices(); }
#5
private function setLegendColors():void { #6 for (var i:int = 0; i < dataProvider.length; i++) { dataProvider.getItemAt(i).legend = _model.getLegendColorForIndex(i); } } private function redrawSlices():void { var currentAngle:Number = 0; var totalUnits:Number = _model.getTotalUnits(_dataProvider);
#7
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
170
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
for (var i:int = 0; i < _view.pieGroup.numChildren; i++) { var slice:PieChartSlice = _view.pieGroup.getChildAt(i) as PieChartSlice; var legendColor:Number = _model.getLegendColorForIndex(i); var arc:Number = i < dataProvider.length ? _model.getAngleForItem( dataProvider[i].units, totalUnits) : 0; // workaround for weird display if only one arc and it's 360 degrees arc = arc < 360 ? arc : 359.99; #8 redrawSlice(slice, currentAngle, arc, legendColor); currentAngle += arc; } _view.pieGroup.draw(null, null); } private function redrawSlice(slice:PieChartSlice, startAngle:Number, arc:Number, color:Number):void { slice.arc.fill = new SolidFill(color, 1); slice.arc.startAngle = startAngle; slice.arc.arc = arc; slice.refresh(); }
#9
} } 1. Private member variables 2. Constructor 3. Set property for dataProvider 4. Event handler 5. Create slices method 6. Set the legend colors on data 7. Redraw slices after update 8. Workaround 9. Redraw the slice
We first define some private member variables to hold onto refrerences to the view and the model, the id of the component for which this presenter belongs to, and the dataProvider for our pie chart (#1). The constructor (#2) for this presenter not only takes in a reference to the view, but also is used to bootstrap the id for the view component since there may be multiple pie charts contained in our application.
We also define an event listener for the
dataProvider being updated in the view component.
Next we define a pair of get/set
properties (#3) for the dataProvider we leverage to update the dataProvider property of the legend data grid whenever the dataProvider for the pie chart is updated. We then define the event handler method for the event that is fired whenever the dataProvider for the view is updated (#4). Inside of this method is where we check to see if the id of the component firing the event is the same as the id that created this presenter.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
171
That way if there are multiple pie chart components, this method can determine whether or not it needs to react. The createSlices (#5) method checks to see if the data provider has more elements contained in it than there are pie chart slices currently in our pie chart. If there are more elements in the data provider, then it will create more pie chart slices. In the setLegendColors (#6) method we iterate through the items in the dataProvider and set the legend property of the item to the corresponding color, which we'll get from the pie chart model class. Once all of that is done, we then refresh our pie chart with a call to the redrawSlices (#7) method.
This will iterate over all of the pie chart slices and update the data values
necessary such as the start angle of the slice and its arc. The reason we iterate over the pie chart slices instead of the data provider is because there may be more slices than items in our dataProvider, and this will then draw the extra slices with an arc of 0. There is also a little workaround (#8) for when there is a single slice and its arc is 360°, which then sets its arc to 359.99 so that it would draw correctly. After all of the data for the slice is updated, it is passed into the redrawSlice (#9) method to tell the slice to redraw itself.
8.3.6 Creating a Model for our PieChart Now we only have one piece of the MVP triad to complete for our pie chart. Even though our pie chart doesn't need to call out to any remote services, we've still refactored a couple of methods that could be considered more business logic than presentation logic and have no need to maintain any kind of state. Listing 8.8 shows the code for the pie chart model.
Listing 8.8 PieChartModel package org.foj.model { import mx.collections.ICollectionView; public class PieChartModel { private var colors:Array = [ 0x468966, 0xFFB03B, 0xFFF0A5, 0x999574, 0x007D9F, 0x8E2800, 0x8E28F4, 0x0528F4, 0xF42105, 0x0CF405, 0xF6FF00 ];
#1
public function getLegendColorForIndex(index:Number):Number { return colors[index]; }
#2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
172
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
public function getAngleForItem(units:Number, totalUnits:Number):Number { #3 return ((((units / totalUnits) * 100) * 360) / 100) ; } public function getTotalUnits(dataProvider:ICollectionView):Number { var total:Number = 0;
#4
for each(var item:Object in dataProvider) { total += item.units; } return total; } } } 1. An array of colors for our legend 2. Convenience method for getting color 3. Calculate the angle for the item 4. Calculate the total number of items for the pie chart
First we've created an array of 10 different hex values (#1) that correspond to the colors we want our pie chart to use for its data points.
This number could easily be increased
should the need arise for more data points in our graphs, however for our example this number should suffice.
Next we define a convenience method (#2) for getting the color
value for a specific index. The method getAngleForItem (#3) takes care of the calculation for determining the size of the angle for an item based on the total number of items contained within the pie chart and the number of items passed in.
The last method we
define (#4) in our model iterates through the data set passed in and returns back the total number of items for the pie chart.
8.4 Adding our PieChart to the application That takes care of all of the pieces we need for the pie chart component. Next we're going to need to make some changes to the GraphView components in order to support it. For our example we'll only add a single instance of this pie chart that we'll be able to change the data it's displaying with a combo box, however you could just as easily display each of these visualizations for the data separately if you'd prefer. We chose this approach because it's an easy way to illustrate the event handling working for the pie chart components because the data will then be updated for the chart each time you select a different reporting point from the combo box.
8.4.1 Updating the GraphView The first thing we'll update to add our pie chart to the application is the GraphView. Earlier in Chapter 2 we created the GraphView with just some simple placeholder text, now we're going to implement this view for real.
Listing 8.9 GraphView.mxml ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
173
#1
#5
#6
#7
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
174
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
1 Components namespace declaration 2 Bootstrap our presenter 3 Refresh button event handler 4 Group by combo box event handler 5 Group by combo box 6 Pie chart 7 Refresh button
Listing 8.9 shows the updated GraphView component after we made the changes to add the pie chart. By now this should be almost second nature. We start out by declaring the namespace for our pie chart components (#1), and bootstrap the presenter in the init method (#2).
Next we define a couple of event handler methods for when the refresh
button is clicked (#3) and when the "group by" combo box is updated (#4). Next we add an HBox to contain a label component as well as the ComboBox (#5) to switch which data field to group the data by in our pie chart (#6). Lastly we add a button (#7) to trigger a refresh on the data being displayed in the pie chart.
8.4.2 Creating a Presenter for the GraphView After we've updated the view component for our GraphView, we need to create the presenter to support it. Listing 8.10 shows the necessary code for the GraphPresenter.
Listing 8.10 GraphPresenter package org.foj.presenter { import mx.collections.ArrayCollection; import mx.controls.Alert; import mx.managers.CursorManager; import mx.rpc.AsyncResponder; import mx.rpc.AsyncToken; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import org.foj.dto.Issue; import org.foj.event.EventDispatcherFactory; import org.foj.event.UIEvent; import org.foj.model.IssueModel; import org.foj.model.GraphModel; import org.foj.view.GraphView; public class GraphPresenter { private var _view:GraphView; private var _issueModel:IssueModel; private var _model:GraphModel; private new { { { {
var _groupByValues:ArrayCollection = mx.collections.ArrayCollection([ label: "Project", data: "project" }, label: "Status", data: "status" }, label: "Type", data: "type" }, label: "Severity", data: "severity" }
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
175
]); public function GraphPresenter(view:GraphView) { this._view = view; this._issueModel = new IssueModel(); this._model = new GraphModel();
#2
EventDispatcherFactory.getEventDispatcher() .addEventListener(UIEvent.REFRESH_GRAPHS, refreshGraphs);
#3
_view.groupByComboBox.dataProvider = _groupByValues;
#4
} private function refreshGraphs(event:UIEvent = null):void { CursorManager.setBusyCursor(); _issueModel.getIssues(new AsyncResponder(getIssuesResult, handleError)); } private function getIssuesResult(event:ResultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); var issues:ArrayCollection = new ArrayCollection();
#5
#6
for each(var item:Object in event.result) { var issue:Issue = new Issue(item); issues.addItem(issue); } _view.issuesPieChart.dataProvider = _model.groupCollectionBy(issues, _view.groupByComboBox.selectedItem.data); } private function handleError(event:FaultEvent, token:AsyncToken = null):void { CursorManager.removeBusyCursor(); Alert.show(event.message.toString()); }
#7
} } 1 ArrayCollection for group by combo box 2 Constructor 3 EventListener for refresh button and combo box events 4 Set the dataProvider for group by combo box 5 Event handler for refresh event 6 Event handler for result from issue model 7 Event handler for errors
We start out by defining an ArrayCollection of items for our combo box (#1). This is an example of the power of a dynamic language such as ActionScript. with JavaScript may recognize this as JSON notation.
Those of you familiar
ActionScript allows us to create
objects like this without any type of Class declaration, which is great for these one off throwaway type objects. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
176
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Next we define our constructor (#2), which takes as an argument, the view component that belongs to this presenter. Inside this constructor we new up an issue model which we use to retrieve the issues from the server side for us, as well as an instance of our GraphModel that we'll create in a moment.
We then add an event listener for the
REFRESH_GRAPHS event (#3) and map it to our handler. The last thing our constructor will do is to set the dataProvider property of the groupByComboBox (#4) so that it has the appropriate display values and data values to make it function correctly.
NOTE The two properties we define for the objects being passed to our combo box have special meaning to this component. First the label property is what will actually be displayed in the combo box for the user to see. The second property, data, is what will actually be returned when using the value property for the combo box, like we did earlier in our change event for the combo box, and click event for the refresh button in our graph view.
We next define an event handler for when the REFRESH_GRAPHS event is triggered (#5). Inside of this handler we make a call to the issue model to fetch the list of issues in the system. In the result event handler for this call (#6) we make a call to our graph model to aggregate the data for us and set the pie chart's dataProvider property to the result from this call. Lastly we define a generic error handler like we have in the other presenters (#7).
8.4.3 Creating the Graph model Now the only thing left to do is implement our Graph model. This model will only contain one method that is used to aggregate and format the list of issues retrieved from the issue model into a format that our pie chart can understand.
Listing 8.11 shows this
implementation.
Listing 8.11 GraphModel package org.foj.model { import mx.collections.ArrayCollection; public class GraphModel { public function GraphModel() { } public function groupCollectionBy(collection:ArrayCollection, field:String):ArrayCollection { var group:Array = new Array(); for each(var item:Object in collection) { if (group[item[field]] == null) { group[item[field]] = 1; } else { group[item[field]]++; }
#1 #2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
177
} var result:ArrayCollection = new ArrayCollection(); for (var key:String in group) { result.addItem({label: key, units: group[key]}); }
#3 #4
return result; } } } 1Array to hold intermediate groupings 2 Iterate over items and create an associative array 3 ArrayCollection for results 4 Iterate over intermediate results and build real results
The implementation for this method is actually fairly simplistic. First we create an array (#1) that will be used to help group and count the items passed in by the property name in the field variable. Next we iterate over the collection of issues (#2), if the array does not contain an entry already with the field name, we add it and set its value to 1. Otherwise we increment the value for that field name. Now we create an ArrayCollection that will contain our real results that we need to return from this method (#3).
We then iterate over the
intermediate results (#4) creating a dynamic object containing the two properties that our pie chart needs to properly display our data: label and units. Lastly we return the result ArrayCollection when we're done.
NOTE About dynamically adding properties...Recall earlier in our PieChartPresenter we are setting a property called "legend" on the results from this function call, however we never define the objects being returned as having a "legend" property.
This is just another
example of how powerful, and sometimes dangerous, a dynamic language such as ActionScript can be. Because we never defined a property named "legend", ActionScript interpreted this to mean, add a property to this object named "legend" and happily continued on its merry little way.
If we would have fat fingered that property name
somewhere else in our code it would make for a very difficult bug to track down.
With all of our components implemented, build and deploy the RIA module to your local repository by issuing a mvn install command on the command line within this module's root folder.
Then change directories to the web module and run the command mvn jetty:run-war to start up the Jetty container so we can see our hard work in action. Keep in mind you may want to actually add the -Dmaven.test.skip=true option to that command so that Maven won't blow away any issues that you had entered into the database. After Jetty starts up, if you don't have any issues created in the application, take a moment to add a few in, then click on the GraphView button in the upper right corner and you should see something similar to Figure 8.3.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
178
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 8.3 The finished graph view
Next change the selected value in the combo box at the top of the page and watch your graph and legend update to display the data grouped by a different data point. If you click back to the DetailsView and add and/or remove some issues you can come back to this view and click on the "Refresh Data" button at the bottom of the screen and it should update your pie chart with the new data from the database.
8.5 Beyond our example When it really comes down to it, there really isn't much that you couldn’t visualize using Degrafa and some ingenuity and imagination. There are some really great examples of the different
ways
you
could
visualize
your
data
http://www.degrafa.org/samples/data-visualization.html.
over
at
the
Degrafa
site
at
Everything from bar charts, to
gauges, financial data, gantt charts, even combining visualization with maps. There was even a presentation at 360|Flex Indy (http://www.flexjunk.com/2009/05/30/developing-a-smithchart-using-axiis-and-degrafa/) recently on creating a Smith Chart using a data visualization framework called Axiis which is built on Degrafa. Figure 8.4 shows some more examples of data visualization components created with Degrafa.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
179
Figure 8.4 More examples of visualization components created with Degrafa
The possibilities of what you can do with Degrafa are endless, in fact someone has even created
a
Growl
like
component
for
use
in
Flex
applications
(http://lukesh.wordpress.com/2009/04/04/rawr-flexgrowl-component-available/),
an
example of this component is shown in Figure 8.5.
Figure 8.5 A Growl like component for Flex ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
180
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
There are some real amazing things that you can do to with Degrafa, whether it be skinning your application and component to look like an iPhone application, or creating a truly rich user experience such as Autodesk's Project Dragonfly shown in Figure 8.6.
Figure 8.6 Autodesk's Project Dragonfly interactive home design software
The Autodesk example, which can be found at http://dragonfly.labs.autodesk.com is one of our favorite examples of what is possible with using the Degrafa library. In the Project Dragonfly app, you can do anything from define your floorplan to moving furniture, doors, countertops and other features all interactively in either a 2D top down view or a full 3D view that can be zoomed and rotated. This is a great example of a truly immersive rich Internet experience; the kind of functionality you would never expect to be available from a web application. You really are only limited by your imagination.
8.5 Summary In this chapter we've introduced the Degrafa framework and created a custom pie chart component. We've also introduced creating a custom ItemRenderer for when you want to display something other than simple text inside of a DataGrid cell. And if that wasn't enough we also demonstrated some of the dynamic features of the ActionScript language by creating some ad hoc objects on the fly and adding properties to those objects at runtime. We've also built this component in such a way that you could now easily refactor it into a separate reusable library. It wouldn't take very much effort to extract the components we created in this chapter into its own module and added as a dependency to any project that you would like to use it in. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
181
9 Desktop 2.0 with AIR
In this chapter:
Creating a common SWC library
Creating an AIR version of a Flex application
Creating a key to sign your application with
Packaging your application for distribution.
Distributing your AIR application via the web
While our Flex application is a very rich and engaging experience, some power users will want a version of the application that they can download and install locally as a desktop application.
Adobe AIR allows us to easily accomplish this otherwise daunting task. Rich
Internet applications blur the line between traditional web applications and desktop client applications, especially when you can convert your Flex application to a desktop application running in the Adobe AIR runtime by changing only a couple of lines of code. Yes, you heard that right, only changing a couple of lines of code. What other framework do you know of that allows you to reuse this much of the client code going from a purely web based experience to a desktop application? We do, however, still want to be able to use the Flex version of our application, so what we're going to do is to refactor all the code that will be common between our Flex application and the AIR application that we're going to build into a separate SWC project. Then we can just declare a dependency on this SWC library in both our AIR project and the Flex project. Once we have the common library project created, we'll create a new maven project for our AIR application, which will create a SWF file that we'll use to package and deploy our AIR application.
Then we will have to create one more project that will contain all of the
resources necessary to package our AIR application, such as the certificate to sign the
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
182
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
application with and any assets that the application will need, for example the icons the OS will use to display the application. Lastly, we'll take a look at what it takes to distribute your application via a web page and how to go about letting the user update the version of their application quick and painlessly. With that being said, let's get on with the show.
9.1 Creating a common library Since the only part of our application that will differ between the Flex version and the AIR version will be the main MXML file, we can refactor all the common classes out into a separate library. That way we can avoid duplicating classes across the two projects.
Figure 9.1 High level view of desired architecture
We're also going to extract the main contents of our Main.mxml into a view component so that the Main.mxml only contains this component. This will allow us to make any changes to the application structure and content in only one place and be able to build both the Flex version of the application and the AIR version without having to change either of the Main.mxml files either of the applications.
Figure 9.1 shows at a high level how the Flex
application and AIR application relate to the view component we're going to extract. So the first thing we need to do is create a new project to contain the common elements of our application.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
183
9.1.1 Creating an SWC project To create our SWC project, we'll use the same archetype that we used to create our Flex application back in Chapter 2. Open a command line and change your current directory to the root of the FlexBugs project hierarchy, and type the following command. mvn archetype:create -DarchetypeGroupId=org.foj \ -DarchetypeArtifactId=flex-mojos-archetype \ -DarchetypeVersion=1.0-SNAPSHOT \ -DgroupId=org.foj \ -DartifactId=flex-bugs-lib \ -Dversion=1.0-SNAPSHOT \ -DremoteRepositories=http://flexonjava.googlecode.com/svn/flexbugs/repository This will create a new folder in your FlexBugs directory named flex-bugs-lib, which contains the new project. Listing 9.1 shows the updated pom.xml for the library project.
Listing 9.1 flex-bug-lib pom.xml 4.0.0 flex-bugs org.foj 1.0-SNAPSHOT org.foj flex-bugs-lib 1.0-SNAPSHOT swc FlexBugs common library src/main/flex src/test/flex flex-bugs-lib org.sonatype.flexmojos flexmojos-maven-plugin ${flexmojos.version} true com.adobe.flex compiler 4.0.0.7219 pom ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
184
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
10.0.0 en_US flexmojos-repository http://repository.sonatype.org/content/groups/public/ flexonjava-repository http://flexonjava.googlecode.com/svn/repository flexmojos-repository http://repository.sonatype.org/content/groups/public/ com.adobe.flex.framework flex-framework 4.0.0.7219 pom com.adobe.flex.framework playerglobal 4.0.0.7219 swc 10 #1 org.degrafa #1 degrafa #1 Beta3.1 #1 swc #1 3.2.0 1. The Degrafa dependency
Thankfully, by using the archetype, the only modification you need to make to the pom.xml is to add the dependency on Degrafa (#1). Everything else is configured for you automatically by the archetype. ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
185
9.1.2 Extracting common classes Now that the project has been created, let's look at what classes need to be moved. As we stated at the beginning of this chapter, the only real file that is going to be different between our Flex application and our AIR version of the application, is the Main.mxml application file at the root of our source directory. So everything else is a prime candidate to move to our new library project.
NOTE If you've checked out the source code from Subversion, or are using Subversion locally on your project, don't just copy and paste the files using the command line or a file explorer window. You will have to use the svn move command from either the command line, a Subversion client tool such as SmartSVN, or inside your IDE if you're using one, otherwise Subversion won't know how to track those files anymore.
Once you have the files moved over to the new project, we need to add a dependency on the new library project we created in the flex-bugs-ria project. Open up the pom.xml file in the flex-bugs-ria project and add the following dependency to the dependencies section. org.foj flex-bugs-lib 1.0-SNAPSHOT swc We treat our common library just as we do any other maven dependency, whether it is for a Java project or a Flex project, the only difference being the type element in our dependency, which in this instance is swc, because our dependency is an SWC library. Now that we've got our common library created, we can now create our AIR application.
9.1.3 Extracting a MainCanvas To avoid having to duplicate the code in the Main.mxml across two separate projects, we're going to extract the majority of that code out into a separate component in our common library project.
Create a new MXML file in the org.foj.view package in the flex-bugs-lib
project and enter the code shown in Listing 9.2.
Listing 9.2 MainCanvas.mxml
#1
#2 #2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
186
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
1. Extends Group 2. Set the layout
The majority of the code in the MainCanvas.mxml is just extracted from the Main.mxml. We start out by declaring this component as extending the Group component (#1), and set it's layout to be vertical (#2). Then we paste in the rest of the code from the Main.mxml.
Listing 9.3 Main.mxml
#1
#2
1. Changed back to mx:Application 2. MainCanvas
Listing 9.3 shows the code that is left over in our Main.mxml after we refactored everything out into the MainCanvas. We start by changing the declaration for our Application ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
187
back to use the mx:Application tag (#1). There appears to be a bug in the latest version of the Flex 4.0 SDK that ultimately prevents the application from running using the newer Spark version of the Application tag. Next we add in our MainCanvas (#2) and set its height and width to 100% so that it fills up any available space in our application window. Now let's continue on and create the AIR application.
9.2 Creating the AIR application The first thing needed in order to create our AIR application is as you may have guessed, creating a Maven project to hold our AIR application. So just as you did earlier, open up a command line, navigate to the root folder for the project and enter the following command: mvn archetype:create -DarchetypeGroupId=org.foj \ -DarchetypeArtifactId=flex-mojos-archetype \ -DarchetypeVersion=1.0-SNAPSHOT \ -DgroupId=org.foj \ -DartifactId=flex-bugs-air \ -Dversion=1.0-SNAPSHOT \ -DremoteRepositories=http://flexonjava.googlecode.com/svn/flexbugs/repository This will create a new module in your FlexBugs project named flex-bugs-air.
The
pom.xml that gets generated for the flex-bugs-air project is shown in Listing 9.4.
Listing 9.4 flex-bugs-air pom.xml flex-bugs org.foj 1.0-SNAPSHOT 4.0.0 org.foj flex-bugs-air swf 1.0-SNAPSHOT FlexBugs Air application 3.3.0 src/main/flex src/test/flex ${basedir}/src/main/resources ${basedir}/target/generated-resources ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
188
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
true flex-bugs-air org.sonatype.flexmojos flexmojos-maven-plugin ${flexmojos.version} true flexbugs 10.0.0 en_US com.adobe.flex compiler 4.0.0.7219 pom flexmojos-repository http://repository.sonatype.org/content/groups/public/ flexonjava-repository http://flexonjava.googlecode.com/svn/repository flexmojos-repository http://repository.sonatype.org/content/groups/public/ com.adobe.flex.framework air-framework 4.0.0.7219 pom org.foj flex-bugs-lib #2 1.0-SNAPSHOT swc
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
189
com.adobe.flex.framework flex-framework 4.0.0.7219 pom com.adobe.flex.framework playerglobal 4.0.0.7219 10 swc 1. The AIR SDK dependency 2. The common library dependency
Just like earlier, we only have to make minor modifications to our pom.xml for the AIR module.
We only have to add our dependency on the AIR framework (#1) and the
dependency on our common library we extracted earlier (#2).
Now we can create the
Main.mxml for our AIR application, which is shown in Listing 9.5.
Listing 9.5 AIR main.mxml
#1
#2
1. WindowedApplication 2. Set the layout 3. MainCanvas
The code for our AIR application should look very familiar. It is almost identical to the code necessary for our Flex application with the one major difference being that the AIR application extends the WindowedApplication (#1) instead of Application like the Flex application does. Lastly we just need to add our MainCanvas to the AIR application (#2). Now that we've got the AIR application complete, let's look at how to go about creating the application installer.
9.3 Packaging your AIR application So now we've got our AIR version of our sample application created and that will generate a different SWF that we can use to package into an AIR application.
In order for us to
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
190
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
distribute this as an AIR application we next need to create a project that will generate an AIR application installer for our AIR application. This will allow you to distribute your AIR application to your users to install by either distributing the .air file that gets created, or as we'll see later in this chapter, by installing it via the web. To package your AIR application, you use a tool called the AIR Developer Tool (ADT). Since this tool is not a part of the open source SDK, you'll need to install the file manually to your local maven repository using the following command. mvn install:install-file -Dfile=adt.jar -DgroupId=com.adobe \ -DartifactId=adt -Dversion=4.0.0 -Dpackaging=jar -DgeneratePom=true Once you've got that library installed into your local repository you're now ready to move on to creating the project that will contain all of the configuration and assets necessary to package your AIR application.
9.3.1 Creating a project to package your AIR app One last time now we need to generate a new maven project that will contain all the assets for creating our AIR application installer package. Open a command line and navigate to the root of your project and enter the following command: mvn archetype:create -DarchetypeGroupId=org.foj \ -DarchetypeArtifactId=flex-mojos-archetype \ -DarchetypeVersion=1.0-SNAPSHOT \ -DgroupId=org.foj \ -DartifactId=flex-bugs-air-package \ -Dversion=1.0-SNAPSHOT \ -DremoteRepositories=http://flexonjava.googlecode.com/svn/flexbugs/repository This will create a new project folder named flex-bugs-air-package. Inside of this project you'll see the standard folder layout that we've been using all along. You can go ahead and delete the src/main/flex folder and the src/test folders, as we won't need them for this project.
9.3.2 Generating a certificate In order to create an AIR application installer you first need to generate a certificate to sign your application with.
The reason behind this is that because you now are running an
application locally on your computer, which now gives the application access to local resources that it would not have had would this still be just a simple Flex application, such as your filesystem.
In light of that, Adobe decided to take measures to secure your AIR
installer by requiring you to sign it with either a self-signed certificate, which we'll be using, or a digital certificate that you would purchase from any certificate authority.
That way
nobody can tamper with your application and introduce malicious software to unsuspecting users. For our example, we're just going to create a self-signed certificate, but if you are planning on distributing your application to the masses, it would probably be a good idea to purchase a certificate from a certificate authority such as Thawte (http://thawte.com). In
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
191
order to generate our certificate we'll use the ADT utility that is included with the Flex/AIR SDKs located in the bin directory of the SDK. The basic usage for this tool is shown below. adt -certificate -cn name [-ou org_unit][-o org_name][-c country] \ key_type pfx_file password This is the basic usage of the ADT utility that we'll use to generate our certificate momentarily. Here are what the abbreviations and options mean:
-cn : The common name of the new certificate
-ou : The org unit (Optional)
-o : The org name (Optional)
-c : The two letter country code (Optional)
key_type: The type of key to create the certificate with, 1024-RSA or 2048-RSA
pfx_file: The name of the certificate file to create
password: The password for the certificate you are creating.
So in order to generate our own self-signed certificate, open up a command prompt and enter the following command. adt -certificate -cn FlexBugs 1024-RSA flexbugs.pfx java4ever This will create a certificate file for us called flexbugs.pfx with a password of "java4ever". You will now need to copy this file into the src/main/resources folder of the flex-bugs-air-package project so that we can sign our application installer with it. Now let's see how we can add some application icons to our application.
9.3.3 Adding some icons Adobe AIR applications allow you to define some icons for the operating system uses for various things like what to display in your dock/taskbar, programs folder, file explorer window, and more. If you don't provide a custom icon set, then your operating system will use a default icon.
There are four common sizes of icons you can include for your
application, which include the following:
16x16
32x32
48x48
128x128
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
192
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 9.x The icon for our application
The icon we've chosen for our application was found at the Wikimedia Commons site at http://commons.wikimedia.org/wiki/File:Green_bug.svg. You can download the icon graphic from the Wikimedia Commons site and create the various icon sizes you need for the application using your favorite image editing software, or if you'd prefer, we've provided the necessary files in the code download for this book.
There is a PNG image file for each
corresponding icon size.
icon16.png for the 16x16 icon
icon32.png for the 32x32 icon
icon48.png for the 48x48 icon
icon128.png for the 128x128 icon
Once you've got the icon files created, copy them into your flex-bugs-air-package project in the src/main/resources/icons folder. Now that we've got all the assets we need to create our AIR package, let's look at the AIR configuration necessary to package our application.
9.3.4 Adding the AIR configuration The last thing we need to do in order to package our application as an AIR application is to create the application configuration file.
To do this, you need to create a file named air-
app.xml and place it in the src/main/resources folder of the flex-bug-air-package project. The AIR application descriptor file is used to define various properties for your AIR application.
The following are a few of the things you can configure using the application
descriptor:
The required AIR runtime version
A unique identifier for your application
The filename and path to use when installing your air application
The application version
The size and display properties of the application window
Any application specific icons
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
193
You can learn more about the other features of the application descriptor that we haven't mentioned at Adobe's website, in the AIR documentation located at the following URL http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ad e46-7ff1.html. Listing 9.6 shows the configuration file we'll use for this application.
Listing 9.6 air-app.xml org.foj.FlexBugs FlexBugs FlexBugs For Desktop 1.0-SNAPSHOT Copyright © FlexOnJava 2009 http://manning.com/allmon flexbugs.swf FlexBugs For Desktop 1024 768 1024 768 icons/icon16.png icons/icon32.png icons/icon48.png icons/icon128.png
#1 #2 #2 #2 #2
#3 #3 #3
#3 #4
#4
1. 2. 3. 4.
AIR application namespace The application metadata The initial window The application icons
The root element of our application descriptor defines the version of the AIR application we're creating, which in this case is version 1.5 (#1). Next comes a few elements defining the application metadata (#2) starting with a unique identifier for our application.
This
identifier is used by the application installer to determine if the application has been previously installed on your computer. Then we define a filename for our application, and provide a descriptive name for the application. We define our version for the application to be 1.0-SNAPSHOT, which also happens to match the version in our pom.xml.
The two
versions are completely unrelated, however it makes sense to keep these two versions in sync with each other. The next section in the application configuration deals with the application and how it behaves when launched (#3).
We start out by defining what the initial content that gets
displayed in our application when it starts up as being the SWF for our AIR application. The title of the window is set to "FlexBugs For Desktop" and we've configured the application to ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
194
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
start out with in a 1024x762 window and also made that be the minimum size the application will run at. Lastly we configure the application to use the icons we created earlier (#4).
9.3.5 Configuring the package build Before we can build and run our application we need to do some tweaking to the pom.xml that was generated for the flex-bugs-air-package module. The pom.xml is rather large so we'll break it down and discuss each plugin separately; the first part is shown in Listing 9.7.
Listing 9.7 flex-bugs-air-package pom.xml 4.0.0 org.foj flex-bugs-air-package 1.0-SNAPSHOT FlexBugs Air Package pom
#1
org.codehaus.mojo exec-maven-plugin 1.1.1 package exec java ${basedir}/target/air -classpath com.adobe.air.ADT -package -storetype pkcs12 -storepass java4ever -keystore certs/flexbugs.pfx ${basedir}/target/air/flexbugs.air air-app.xml
#2
#3 #4
#5
#6 #6 #6 #6 #6 #6 #6 #6 #6 #6
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
flexbugs.swf icons/icon16.png icons/icon32.png icons/icon48.png icons/icon128.png 1. 2. 3. 4. 5. 6.
195
#6 #6 #6 #6 #6
Packaging type exec-maven-plugin The phase to execute in The goal to run The executable The command line arguments
We start out the pom just like we did for our shared BlazeDS configuration earlier in Chapter 5, setting the packaging type to pom (#1).
Unfortunately the Flex-Mojos plugin
we've been using all along for building our Flex application does not include a plugin for packaging our AIR application, so we have to use the ADT utility provided by Adobe in the SDK.
To do this we need to use a Maven plugin called exec-maven-plugin (#2).
We
configure the plugin to get executed during the package phase of the Maven lifecycle (#3), and to execute the exec goal (#4). We'll be executing the java executable (#5) to utilize the ADT utility (#6) that we described earlier. The first set of arguments we pass into the ADT utility deal with the certificate we generated earlier in this chapter. Next we specify what our output file should be, where to find the AIR application configuration, the SWF file to include in our AIR application and finally the application icons to use.
Listing 9.8 flex-bugs-air-package pom.xml continued org.apache.maven.plugins maven-assembly-plugin Package AIR distribution single package src/main/assembly/resources.xml
#1
#2 #3
#4
1. The assembly plugin 2. The goal to execute
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
196
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
3. The phase to execute in 4. The location of the resource descriptor
Listing 9.8 shows the configuration for creating an assembly for our AIR installer. It looks very similar to the configuration we used earlier in Chapter 5. The plugin configuration starts out by defining that it is the maven-assembly-plugin (#1). We'll be calling the single goal (#2) during the package phase of the Maven lifecycle (#3). Lastly we tell the plugin where to find the assembly descriptor (#4). Next we'll configure Maven to copy the files we need to a working directory for the ADT utility to use when building our AIR installer. Listing 9.9 shows this configuration.
Listing 9.9 flex-bugs-air-package pom.xml continued org.apache.maven.plugins maven-resources-plugin #1 air generate-resources #2 copy-resources #3 ${basedir}/target/air #4 ${basedir}/src/main/resources #5 1. 2. 3. 4. 5.
The maven-resources-plugin The phase to execute in The goal to execute The output directory to copy resources to The resource directory
Earlier we configured the ADT utility to use a working directory of target/air to do its work in. To get all the resources we need into that directory, we leverage the maven-resourcesplugin (#1) to do this for us.
We configure the plugin to execute its copy-resources goal
(#3) during the generate-resources phase of the Maven lifecycle (#2) and tell it to copy the resources from the src/main/resources directory (#5) to the target/air directory for us (#4).
Listing 9.10 flex-bugs-air-package continued maven-dependency-plugin unpack-air-assets
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
197
copy generate-resources org.foj flex-bugs-air 1.0-SNAPSHOT swf false ${basedir}/target/air flexbugs.swf 1. 2. 3. 4. 5.
#2 #3 #4
#5
The maven-dependency-plugin The goal to execute The phase to execute in Using an artifact item The output directory
Listing 9.10 shows the configuration for unpacking the AIR SWF to be used by the ADT utility when building the AIR installer.
To do this we'll use the maven-dependency-plugin
(#1), just like we do for copying the Flex SWF into our WAR before packaging. We configure this plugin to execute the copy goal (#2) during the generate-resources phase of the Maven lifecycle (#3). The thing it's going to copy is the artifact we define in its configuration (#4). We tell the plugin to copy the artifact to the target/air directory (#5) so that it can be included when creating the AIR installer.
Listing 9.11 flex-bugs-air-package pom.xml continued maven-clean-plugin 2.2 target
#1
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
198
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
com.adobe adt 4.0.0 jar org.foj flex-bugs-air 1.0-SNAPSHOT swf
#2
#3
1. Maven-clean-plugin 2. ADT dependency 3. AIR dependency
Listing 9.11 shows the rest of the pom.xml for our packaging project. We configure one last plugin, the maven-clean-plugin, to clean up after ourselves (#1). Then the only thing left to do is to decalre our dependencies. The first dependency is on the ADT utility (#2), so that we can use that to build our AIR installer. The second is on the SWF artifact for our AIR application (#3), so that we can include it in our AIR installer. With all of those pieces in place, you should now be able to build and install your AIR application.
Open up a command line and navigate to the main project folder, then build
your application by typing mvn clean install. This will go through and build each of the project modules.
When it's finished, change into the flex-bugs-web directory so you can
startup the web application using the mvn jetty:run-war command. Once your web application is running, open up a file explorer window and navigate to the
target/air directory in your flex-bugs-air-package project. Inside this folder there should be a file named flexbugs.air. This is the installer for your AIR application. Launch the installer and you should be presented with a screen that looks like Figure 9.2.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
199
Figure 9.2 Installing your AIR application
Since we're using a self-signed certificate, the installer warns you that it's not sure who wrote the application and whether or not they're trustworthy.
Once you click the Install
button you will be presented with a screen that looks like Figure 9.3.
Figure 9.3 Choosing where to install the application
On the second screen of the installer, it asks you if you'd like to launch the application when it's done installing, and where you'd like to install the application to. If you left the
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
200
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
checkbox to "Start application after installation" checked, then the application should launch and you should see something that resembles Figure 9.4.
Figure 9.4 The finished AIR application
Now you've got a working version of the FlexBugs application that works outside of the browser.
However you're still stuck with the task of distributing this application to your
users. In the next section we'll take a look at the task of creating a webpage to allow you to distribute the AIR application using an installer badge, which allows your users to install the application simply by clicking on the badge.
9.4 Distributing your AIR application One of the appeals to using a web application is the ease of being able to distribute your application to the masses that will be using it.
Adobe has provided a way for you to
streamline the distribution and installation process for AIR applications by providing a SWF that will allow your users to install your AIR application simply by clicking on a badge icon that you can place on any web page. In this next section we'll create an installer badge for our sample application.
9.4.1 Assembly configuration The first thing we need to do in order to distribute our AIR application, is to somehow get our application installer into a place where our users can access it via they're web browser. Thankfully we already have this mechanism in place.
We can leverage our existing web
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
201
application and copy our AIR artifact into our WAR file that will be deployed to the application server, which also happens to be where we would create the download page anyway. To do this we're going to take a similar approach to how we handled the shared BlazeDS configuration in Chapter 5.
Since Maven doesn't know how to handle .air files, we can
leverage the assembly plugin.
Listing 9.12 shows the resources.xml file we'll use to help
create our assembly.
Listing 9.12 resources.xml resources zip false target/air **/*.air
#1
#2
1. The format to create 2. Only include .air files
Similar to how we did earlier, we configure our assembly to be of type zip (#1).
The
main difference between this resource configuration and how we did it in Chapter 5 is that we're configuring our assembly to only contain the .air file which is created by the ADT utility (#2). Next we need to update our build to be able to unpack our assembly.
9.4.2 Updating build To be able to get the AIR installer into our web application we need to be able to extract our AIR application out of the assembly we just configured.
To do this we will be creating
another execution in our maven-dependency-plugin section similar to how we get our shared BlazeDS configuration into our WAR. Listing 9.13 shows the configuration we'll add to the pom.xml in our flex-bugs-web project.
Listing 9.13 flex-bugs-web pom.xml ... install ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
202
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
flexbugs maven-dependency-plugin unpack-blaze-config ... unpack-air-package unpack-dependencies generate-resources ${project.build.directory}/${project.build.finalName} ${project.groupId} flex-bugs-air-package resources true jar,swf ...
#1 #2
#3
#4
... 1. 2. 3. 4.
The unpack dependencies goal The phase to execute in Where to unpack the file to Which artifact to include
First we create another execution inside the maven-dependency-plugin configuration. We configure this execution to execute the unpack-dependencies goal (#1) during the generateresources phase of the Maven lifecycle (#2).
We want the plugin to extract our AIR
application and put it in the working directory that Maven uses when packaging up our WAR file (#3).
We also need to configure the plugin to make sure that it only includes the
artifactId we want it to unpack (#4). Once that is configured, we need to add the dependency for our assembly in the dependencies section of the pom.xml in the flex-bugs-web project.
Add the following
dependency to the pom: org.foj flex-bugs-air-package 1.0-SNAPSHOT resources provided ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
203
zip With that in place, now we can move on to creating the download page which will contain our installer badge.
9.4.3 Creating a download badge We're going to create another JSP page in the flex-bug-web project to contain our install badge. Create a new file inside the src/main/webapp folder called download.jsp, and enter the code shown in Listing 9.14.
Listing 9.14 download.jsp FlexBugs AIR application #1 // To install this application you will need the Adobe Flash Player
#4
1. The swfobject javascript include
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
204
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
2. Flashvars 3. Call embedSWF 4. Placeholder div
The download page is using a JavaScript library that makes embedding Flash objects in our web page easier called swfobject (#1), which can be found at Google Code here (http://code.google.com/p/swfobject/) if you're interested in learning more about it.
Next
we define a variable called flashvars to hold the necessary configuration items (#2). Then we use a little bit of JSTL expression language to help us build up a fully qualified link for our AIR application adding that to our flashvars.
The badge installer seems to require a fully
qualified link to the AIR installer for some reason, so we were forced to use this. The beauty of using the JSTL instead of just hard coding the URL in, is that no matter where we deploy this application now, that link will always work, instead of having to update it for each environment we deploy to. Next we tell the badge installer which icon to display in the badge so that we can customize the badge with our own application's branding. We then create our badge using the swfobject library by calling the embedSWF function (#3) and pass in a bunch of arguments telling it first, what SWF file to create our badge from. Next we tell the badge which div we want it to replace the content with our badge, what the size we want our badge to be and what the minimum version of Flash is supported by our application. If we were providing an installer to our user to install the AIR framework from, we could specify that argument here as well. Lastly we provide some default information in our div that lets our user know that this page requires Flash in order to use the installer badge, and a link to download the Flash runtime (#4).
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
205
Figure 9.5 The install badge in action
Now that we have all of our pieces in place, open up a command prompt and navigate to the root of your project and build the application by typing mvn clean install. When that is done, change directories into the flex-bugs-web project and let's start up the Jetty container by running mvn jetty:run-war.
When the container is finished loading open up a browser
and navigate to http://localhost:8080/flexbugs/download.jsp and you should see a screen similar to Figure 9.5. When you click on the install badge it should prompt you to install the AIR application.
9.5 Summary We now have successfully broken free of our browser and created an application that our power users can use as a standalone application.
Even though it took an awful lot of
configuration, you'll notice that we never had to change much of the actual application code in order to get this to work.
There are not very many programming languages or
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
206
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
frameworks that will allow you to so easily reuse your code to create both a web based client as well as a desktop client.
This is one of the very attractive features of both the Flex
framework and the Adobe AIR framework.
You'll discover that many of your Flex
applications can be translated into AIR applications with minimal effort. The Adobe AIR SDK offers much more functionality than you can accomplish with the Flex framework alone, so if you're going to create an AIR application, you may not want to limit yourselves to only the Flex components and APIs.
Some of the things that the AIR
application framework offers you are the ability to store information in an embedded database so that you could potentially create an offline capable version of your application that could sync up the local database with the master database when the network becomes available again. You also have the ability to read and write files to the file system and even define new file associations so that they automatically open with your application. A fine example of this is the Balsamiq application (http://www.balsamiq.com/), which we've been using throughout the book to create our wireframes for the sample application. This application allows you to save and export your wireframes in various formats, and even defines a file association such that whenever you try to open a file with the extension .bmml, it will by default launch the Balsamiq application. In the next chapter we're going to now show you how you can test your application to verify that it indeed does what we expect it to do.
We'll introduce you to the FlexUnit4
library and also the mock-as3 library for mocking your dependencies.
Now, on to the
testing.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
207
10 Testing your Flex application
In this chapter:
Unit testing with FlexUnit
Mock testing with mock-as3
Leveraging the hamcrest-as3 matchers
Generating a test report
No good programming book should ever be without a discussion on unit testing. Whether you follow the practice of test driven development, which we the authors are advocates of, or write your tests after the fact, an automated suite of unit tests is a very valuable thing to have to accompany your application.
Not only does a comprehensive suite of unit tests
prove to your customer that the code you wrote does indeed do the things that it's supposed to, it also provides developers that may have to make changes to your code down the road with both the confidence that the changes that they're making will not break anything else in the application and an up to date form of documentation in the form of unit tests. Written documentation can very easily get out of sync with the actual implemented code, however a good suite of passing unit tests should always be in sync with what the code is actually doing. You can think of your unit tests as being a form of executable documentation that developers can use in the years to come.
10.1 Unit testing and TDD If you are not familiar with the practice of Test Driven Development, or TDD as you'll often see it referred to as, Figure 10.1 shows the basic workflow. In a nutshell, you start off by writing a failing unit test; then write only enough implementation code to make it pass. When you get a passing test, you start the process all over again.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
208
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 10.1 TDD workflow
There are a couple of very interesting side effects of writing code this way, the first obvious one being that your code is more likely to be written in a testable manner, since you're starting with the tests first. Another not so obvious effect of practicing TDD is that you only add code that is absolutely necessary and less speculative coding occurs. If you would like to know more about test driven development there is a whole slew of books out there
on
the
topic,
including
Lasse
Koskela's
Test
Driven
(http://www.manning.com/koskela/). Of course it is still easy to write code that you don't need in your application, you'll just have extraneous code that is well tested, that's all. One good way to ensure that only code and features that are relevant and required in your application is to practice a more refined technique of test driven development called Presenter First, which is the main motivation behind the Model View Presenter pattern that we introduced back in Chapter 4. The thinking behind the Presenter First approach is that your presenter tests map very closely to your user stories and if you only write tests that map to your user stories, you'll avoid much of the gold plating that we developers are often guilty of doing by sneaking features that we think would be useful, but don't appear in any of the requirements set forth by the customer. For a more thorough explanation of the Presenter First approach to development you can read the
article
printed
in
Better
Software
magazine
at
http://www.stickyminds.com/s.asp?F=S11947_MAGAZINE_2. In this chapter we'll be utilizing the unit-testing framework FlexUnit, and more specifically the latest incarnation of the FlexUnit4, which brings the framework more in line with current ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
209
unit testing frameworks available for Java such as JUnit4. Some of the features that were introduced with this newest version of FlexUnit are:
No longer having to inherit from the TestCase base class.
The addition of annotations for adding metadata to your test cases.
Easier asynchronous testing
Introduction of hamcrest matchers
For a comprehensive listing of all the new features in FlexUnit4, check out the documentation
that
is
available
http://opensource.adobe.com/wiki/display/flexunit/FlexUnit+4+feature+overview.
at We'll not
get into a lengthy discussion on FlexUnit and how to use the FlexUnit framework, instead, we'll discuss the features as we introduce them in our examples. One of the main purposes of unit testing is to try to isolate your scope of testing to as narrow of a scope as you can, which is why it's called unit testing. In order to do that you can utilize a mock testing framework in order to remove any external dependencies the class you are testing has. By injecting these mocks into your class during unit testing, you are able to now test how your class interacts with its external dependencies without relying on those external dependencies to exist. The mock testing framework we'll be utilizing in this chapter is callled mock-as3.
The mock-as3 project is hosted via Google Code at
http://code.google.com/p/mock-as3/. In order to get started unit testing in our Flex application, we'll need to make some changes to the project. So let's go ahead and get started.
10.2 Updating the project Before we can start with writing our first unit test, we'll need to update the pom.xml for our flex-bugs-lib project to add the necessary unit testing libraries as dependencies and also the unit testing support for the FlexMojos plugin that we're utilizing for our builds. Listing 10.1 shows the updates that we need to add to our pom.xml.
Listing 10.1 Updated pom.xml ... src/main/flex src/test/flex
#1
... ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
210
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
... ... org.sonatype.flexmojos flexmojos-unittest-support ${flexmojos.version} swc test com.adobe.flexunit flexunit 4.0-beta-2 swc test org.hamcrest hamcrest-as3 1.0 swc test mock-as3 mock-as3 1.0.0 swc test
#2
#2 #3
#3 #4
#4 #5
#5
... org.apache.maven.plugins maven-surefire-report-plugin 3.4.2
#6
#6
1 The test source directory 2 FlexMojos testing support 3 FlexUnit dependency 4 Hamcrest matcher dependency
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
211
5 Mock-as3 dependency 6 Test report plugin
The first section of the pom.xml that needs to be modified is the section that tells Maven where our test sources are.
We override the default location of src/test/java with the
location of src/test/flex (#1) to more closely follow the convention we are following for our source code.
Next we need to add a dependency on the FlexMojos unit testing support
library (#2).
Then we add dependencies on the FlexUnit4 (#3), the hamcrest matcher
library (#4), and the mock-as3 library (#5).
You may also notice that we've defined the
scope of these dependencies as having test scope. That way they will not be included in the artifact that ultimately gets deployed to your application server when you deploy your application.
The last thing we added to the pom.xml is the reporting plugin (#6), which
gives you the ability to generate test reports for your unit tests. The dependencies for the unit testing support and FlexUnit4 exist on the Sonatype Maven repository, however the hamcrest and mock-as3 dependencies do not.
You can either
download those libraries and install them manually using the mvn install:install-file plugin or utilize the maven repository we have setup for the sample code in our book using the Google Code repository. Now that we've got the project setup and ready for testing, we'll start looking at how to unit test our Flex application.
To illustrate this we're going to test the MasterPresenter,
MasterView and IssueModel. These classes are simple enough for us to keep our examples short and to the point, yet contain enough functionality in them to properly illustrate most of the situations you'll need to test. To get started we'll begin with testing the MasterPresenter.
10.3 Testing the Presenter We've chosen to start our unit testing examples with testing the presenter. We like to start with testing the presenter, because as we stated earlier the presenter typically maps closest to your user stories, and also the presenter often times will be the more complex part of the MVP triad since it has more responsibility. The first thing we'll need to do is to create the unit
test
class
for
our
MasterPresenter.
Create
the
package
structure
for
the
org.foj.presenter package under the src/test/flex folder inside the flex-bugs-lib project. Next create the MasterPresenterTest.as class inside this org.foj.presenter package. The first part of the code for the MasterPresenterTest class is shown in Listing 10.2.
Listing 10.2 MasterPresenterTest.as package org.foj.presenter { ... public class MasterPresenterTest { public var target:MasterPresenter;
#1
public var mockery:Mockery; public var view:MasterView;
#2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
212
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
public var model:IssueModel; public var mockResponder:IResponder;
#2
public var dispatcher:EventDispatcher;
#3
[Before(async, timeout=5000)] public function setUp():void { mockery = new Mockery(); mockery.prepare(MasterView); mockery.prepare(IssueModel);
#4
dispatcher = EventDispatcherFactory.getEventDispatcher(); Async.proceedOnEvent(this, mockery, Event.COMPLETE, 5000); } [After] public function tearDown():void { mockery.verify(model, view); }
#5
} } 1 The testing target 2 Our mocks and mock control 3 EventDispatcher 4 setUp method 5 tearDown method
For those of you who are familiar with Java and JUnit this should look very familiar. We name our test class with the convention of ClassNameTest because this is the default pattern that Maven will use to look for test classes when running our tests during the build process. We start out with defining a few private member variables for the class we are trying to test (#1), the mock control and the mock objects we'll need to interact with (#2), and lastly our good friend the event dispatcher (#3).
Since most of our classes will use the event
dispatcher from the EventDispatcherFactory, we'll also need to use this in our tests then to ensure that our events are properly dispatched and handled. Next we create a setup method that will get run at the beginning of each test in our class (#4). We named our method setUp merely out of habit, you could name it whatever you would like. In the previous version of FlexUnit you had to override the setUp method from the base class, now the framework makes use of the [Before] annotation to know which method to use for its setup.
We also add some more metadata to the annotation to tell
FlexUnit that this test will use some asynchronous functionality and that it should timeout after 5 seconds. This illustrates one of the potential pitfalls of testing with FlexUnit. If your unit tests are running long you may end up with some false positive failures and you'll find that periodically you'll have tests that will timeout and fail for no reason. Inside of our setup method we setup our mock control by simply instantiating a new Mockery class.
Then we need to tell our mock control to prepare our mocks by calling
prepare for each class that we'll be mocking. The last thing we do in our setup is to tell it to ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
213
wait until the mock control is done preparing by calling the proceedOnEvent method and setting the event we want to proceed on to the complete event from our mockControl. Then we define the counter part to our setup method, the teardown method (#5). Just like we did for the setup, we follow the convention of calling this method tearDown simply out of habit, the [After] annotation is what tells the framework to run this after every test method run. The only thing that we do inside our teardown method is to call verify on our mocks to ensure that everything we expected our mocks to call was indeed called.
10.3.1 Testing refresh issues Now that we've got the skeleton for our MasterPresenterTest written, let's start with our first test. The first piece of functionality we're going to test would probably come from a user story that would read something like: In order to ensure that the issues I'm viewing are up to date, when I click on the "Refresh" button, the application should retrieve the issues from the server and refresh the data grid with the results. The important part to take away from the story above are the "when's". These are the things that will need to be tested by our presenter tests. The first "when" that we will be testing is the part about the "when I click on the 'Refresh' button, the application should retrieve the issues from the server" part. The test for this is shown in Listing 10.3.
Listing 10.3 Refresh Issues test [Test] public function refreshIssues():void { model = mockery.strict(IssueModel) as IssueModel; view = mockery.strict(MasterView) as MasterView;
#1 #2 #2
mockery.mock(model).method("getIssues").withArgs(IResponder).once;
#3
target = new MasterPresenter(view, model);
#4
var event:UIEvent = new UIEvent( UIEvent.REFRESH_ISSUES_BUTTON_CLICKED ); dispatcher.dispatchEvent(event);
#5
} 1 Test annotation 2 Create the mocks 3 Mock expectation 4 Create the target 5 Create and dispatch event
We start out by annotating our test method with a [Test] annotation (#1).
FlexUnit4
utilizes this [Test] annotation to know which methods are our test methods. Next we create our mocks by calling the strict method on our mock control (#2) passing in the class that we want to mock. We use strict mocks here so that if any methods get called on our mocks that we don't explicitly setup our mock control will fail the test in the teardown when we call ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
214
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
verify. We then setup the expectation on our mock (#3). The expectation syntax for mockas3 is fairly self-explanatory, and most of the time will look very similar to this. Let's take a minute to analyze this line of code.
The first thing it says is we're going to mock on the
model, which is the mock for the IssueModel class.
We're expecting our code to call the
method called "getIssues", and we're going to pass in arguments of type IResponder and expect it only once.
You could just as easily put in a concrete value or object in this
expectation and the mock framework would test the equality of what the method is actually called with.
In this instance we can't very easily try to perform an exact match on the
IResponder, we are more interested in whether or not the method is called or not. Next we create our target by calling the constructor and passing in the mocks that we created (#4). Lastly we simulate the behavior that our view will exude. We create a new event of type "REFRESH_ISSUES_BUTTON_CLICKED" and then dispatch it (#5). In order for our code to compile we need to make a change to the constructor for our MasterPresenter class to allow us to inject both the view and the model. Listing 10.4 shows the updated constructor for the MasterPresenter.
Listing 10.4 updated MasterPresenter public function MasterPresenter( view:MasterView = null, model:IssueModel = null) { if (model != null) { this._issueModel = model; } else { this._issueModel = new IssueModel(); } this._view = view;
#1 #1 #2
#3
... } 1 Constructor parameters 2 Setting the model 3 Setting the view
We first have to add in the ability to pass in an IssueModel to the MasterPresenter as well as the MasterView (#1). We also are defaulting it to null, which means that we don't need to change any of the code that we wrote earlier that doesn't pass in this extra parameter. That's one of the nice things about being able to specify default parameters. Next we add a block of code to set the issueModel to either the value passed into the constructor, or instantiate a new one if nothing was passed in (#2). Lastly we set the view that was passed in (#3). Now you can run the unit test and hopefully it should pass. Open up a command line and navigate to the flex-bugs-lib folder and type mvn test. After all of the messages scroll by you should see the results from the tests being run.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
215
10.3.2 Issues result event test Now we can test the second half of our user story that we showed in the previous section. To find the next when in that user story, we could rewrite the story to read more like this: In order to ensure that the issues I'm viewing are up to date, when I click on the "Refresh" button, the application should retrieve the issues from the server and when the result comes back from the server, refresh the data grid with the results. In an ideal world we would be able to capture the responder object passed into our mock in the test we just wrote, however as of right now, the mocking framework that we're using doesn't support that. So in order to overcome this we need to scope the methods that our result events call to the public scope so that we can call it directly. Listing 10.5 shows the test for the result from the call to the getIssues method on the IssueModel.
Listing 10.5 getIssues Result Test [Test] public function getIssuesResult():void { model = mockery.strict(IssueModel) as IssueModel; view = mockery.strict(MasterView) as MasterView; var issue1:Issue = new Issue(); issue1.id = 1; var issue2:Issue = new Issue(); issue2.id = 2;
#1 #2 #2 #3
var issues:ICollectionView = new ArrayCollection([issue1, issue2]); mockery.mock(view).property("issues").withArgs(ICollectionView).once; #4 target = new MasterPresenter(view, model);
#5
var resultEvent:ResultEvent = new ResultEvent(ResultEvent.RESULT, false, true, issues); target.getIssuesResult(resultEvent);
#6
} 1 Test annotation 2 Create mocks 3 Mock result data 4 Mock property access 5 Create target 6 Simulate result event
We start out just like we did in the last test by annotating the method with the [Test] annotation (#1). Next we create our mocks by calling strict on the mock control (#2). Next we create some mock result data by creating a couple of Issue objects and create an ArrayCollection to hold our results (#3). Not only can we mock method calls on our mocks, but we can also mock property access, so we mock out access to the set property "issues" (#4). With all of the expectations set, we can now instantiate our presenter (#5) with our ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
216
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
mocks. Lastly we create a ResultEvent to simulate the model calling the event handler and call the getIssuesResult passing in the ResultEvent.
10.3.3 Testing issue removed The next thing we want to test is what happens when an issue gets deleted. This feature may be described something like the following: In order to remove issues that may be invalid or incorrect, when the user clicks on the remove issue, the application should delete the selected issue. Again, the important part of the user story is what's described after in the "when" part of the description.
In this case we want to simulate some other part of the application
dispatching an event indicating that the "Delete Issue" button has been clicked. The code for this test is shown in Listing 10.6.
Listing 10.6 remove issue test [Test] public function removeIssue():void { var selectedIssue:Issue = new Issue(); selectedIssue.id = 123;
#1
model = mockery.strict(IssueModel) as IssueModel; view = mockery.strict(MasterView) as MasterView;
#2 #2
mockery.mock(model).method("removeIssue") .withArgs(selectedIssue.id, IResponder).once;
#3
target = new MasterPresenter(view, model);
#4
var event:UIEvent = new UIEvent(UIEvent.REMOVE_ISSUE_BUTTON_CLICKED); #5 event.data = selectedIssue; dispatcher.dispatchEvent(event); } 1 Test annotation 2 Mock objects 3 Mock expectation 4 Instantiate target 5 Create and dispatch event
This test looks follows the same pattern as the first one we wrote.
We start out by
annotating our test with the [Test] annotation (#1). Next we create an issue local variable that we'll utilize in our mock expectation. method on the mock control (#2).
Then we create our mocks by calling the strict
After we've created our mocks we can define our
expectation of the removeIssue method on the model being called (#3). This expectation will take an actual value in trying to match the value of the id property on the issue we created in the test, as well as an argument of type IResponder. Next we instantiate our test target (#4). In this test we are passing along some data along with the event that we need ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
217
to dispatch (#5), so we instantiate a new event and set its data property to the issue we created in our test and dispatch it.
10.3.4 Remove issue result test Now that we have the first part of the "Remove Issue" functionality complete, we need to test what happens when we receive the ResultEvent from the IssueModel. This test will be similar to how we tested the result for the "Refresh Issues" button being pressed earlier. The code for this test is shown in Listing 10.7.
Listing 10.7 removeIssue Result Test [Test(async, timeout=8000)] public function removeIssueResult():void { model = mockery.strict(IssueModel) as IssueModel; view = mockery.strict(MasterView) as MasterView;
#1 #2 #2
mockery.mock(view).method("resetIssuesGrid").withNoArgs.once; mockery.mock(model).method("getIssues").withArgs(IResponder).once;
#3 #3
Async.proceedOnEvent(this, dispatcher, UIEvent.SELECTED_ISSUE_CHANGED, 8000);
#4
target = new MasterPresenter(view, model);
#5
var resultEvent:ResultEvent = new ResultEvent(ResultEvent.RESULT); target.removeIssueResult(resultEvent);
#6 #6
} 1 Test annotation 2 Create mocks 3 Mock expectations 4 Expected event 5 Instantiate target 6 Call the target
The first thing you may notice is that the [Test] annotation looks a little different than the tests we've written up to this point (#1). It looks more like the setup method we defined back in Listing 10.x, with the extra metadata stating that this method will make use of the Async.proceedOnEvent method and that it should timeout after 8 seconds. Next we create our mocks just as before, by calling the strict method on the mock controls (#2), along with setting the mock expectations that we'll call the resetIssuesGrid method on the view (which you may need to stub out in order to get the test to compile), and that we'll call the getIssues method on the model in order to refresh the data grid (#3). The call to getIssues will actually occur as a result of the SELECTED_ISSUE_CHANGED event that we're expecting to get dispatched in the event handler, and we define that expectation next (#4).
This call to the proceedOnEvent method allows us to define an
expectation on the test method that will fail unless the presenter dispatches the event we pass in as an argument to the proceedOnEvent method. You'll notice that we're using the dispatcher from the EventDispatcherFactory in this expectation as well. This is because our ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
218
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
application will use this to dispatch its events, and in order for our test to work as expected, it'll need to utilize the same event dispatcher that the application will use. The next step is to instantiate our presenter passing in the mocks we created at the beginning of this unit test (#5). Lastly we create a ResultEvent to call our method that we're testing and invoke it. Now all that's left to do is to run the tests again. Open up a command line and navigate to the flex-bugs-lib project and run the mvn test command. When the smoke clears, you should be presented with a method telling you that you now have 4 passing tests. We have merely scratched the surface with unit testing the presenters in this application; however the examples we presented should be enough firepower for you to continue to write all of the tests necessary for the presenters.
We're now going to move along to testing our view
components.
10.4 Testing the View Now that we've shown how to test your presenters, it's time to move on and unit test our view components. When we say unit test our view components, we're not talking about in browser functional testing. For that level of testing you'll want to look at either the FunFX (http://funfx.rubyforge.org/)
testing
framework
or
the
Selenium-Flex-API
(http://code.google.com/p/sfapi/). Instead we're simply talking about testing the public API exposed by our view components to the rest of the application. Again, we'll start by creating our test class, which is shown in Listing 10.8.
Listing 10.8 MasterViewTest package org.foj.view { ... public class MasterViewTest { private var target:MasterView; private var eventDispatcher:EventDispatcher;
#1 #2
[Before(async,ui)] public function setUp():void { target = new MasterView(); target.initialize(); eventDispatcher = EventDispatcherFactory.getEventDispatcher(); }
#3
#4
} } 1 Test target 2 Event dispatcher 3 setUp method 4 Initialize target
We start this test by defining a couple of private fields for our test target (#1) and our event dispatcher (#2). Then we define a setUp method (#3) to be called before each test ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
219
that runs. Inside of the setup method we instantiate a new MasterView and initialize it by calling the initialize method (#4). We do this because MXML components have a lifecycle of their own, and when your application starts and when components are added to your application Flex will call these lifecycle methods and generate events for us. So in order for our component to properly initialize itself and all of the child components, we need to manually call the initialize method. One thing you may notice is that the test for our view component doesn't include any mocks. Since this view component doesn't have any external dependencies, this test will be more of a stateful test as we assert that the state of the components contained within our view change as we expect them to when calling the public methods.
10.4.1 Testing the issues set property The first thing we'll be testing in the MasterView is the set property for the issues data grid. By default any child components that are added to your MXML component have public access applied to them, so we could very easily just allow our presenter to access the child components directly since it has a reference to the view. However, not only is this not very easily testable, it also violates the Law of Demeter (http://c2.com/cgi/wiki?LawOfDemeter) by allowing our presenter to call methods and properties on the child components of our view.
Listing 10.9 Set Issues test [Test] public function testSetIssues():void { var dataProvider:ArrayCollection = new ArrayCollection(); var issue1:Issue = new Issue(); issue1.id = 1; dataProvider.addItem(issue1);
#1
var issue2:Issue = new Issue(); issue2.id = 5; dataProvider.addItem(issue2);
#1
target.issues = dataProvider;
#2
assertThat(target.masterViewDataGrid.dataProvider, equalTo(dataProvider));
#3
} 1 Dummy data 2 Call issues set property 3 Assert the state
The code for our first view test is shown in Listing 10.9. We start our test by creating an ArrayCollection with a couple of Issue objects to call the issues set property with (#1). Then we call the issue set property and set it to the ArrayCollection we just created (#2). Lastly we assert that the dataProvider property of the masterViewDataGrid is the same as the ArrayCollection we passed in (#3). This last line shows the Hamcrest matcher syntax, and
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
220
Allmon and Anderson / Flex on Java
just how readable it makes our assertions.
Last saved: 1/6/2010
You can clearly gather from that line of code
what it is that we expect to happen.
10.4.2 Testing resetIssueGrid function There is one other public method that we want to test for the MasterView, the resetIssuesGrid method. This method will make sure that there is not a selected item on the data grid by resetting its selected index property to -1. The code for this test is shown in Listing 10.10.
Listing 10.10 Test resetIssuesGrid [Test] public function testResetIssuesGrid():void { var dataProvider:ArrayCollection = new ArrayCollection(); var issue1:Issue = new Issue(); issue1.id = 1; dataProvider.addItem(issue1);
#1
var issue2:Issue = new Issue(); issue2.id = 5; dataProvider.addItem(issue2);
#1
target.issues = dataProvider; target.masterViewDataGrid.selectedIndex = 2;
#2 #3
target.resetIssuesGrid();
#4
assertThat(target.masterViewDataGrid.selectedIndex, equalTo(-1)); #5 } 1 Dummy data 2 Set the issues 3 Select an issue 4 Call resetIssuesGrid 5 Assert the grid reset
We start out this test by creating an ArrayCollection to simulate our data grid having some data (#1) and then set the issues property of our view to the sample data we created (#2).
Next we select an item on the grid by setting the selectedIndex property of the
datagrid to 2 (#3).
Then we call the method we are testing (#4) and assert that the
selectedIndex of the datagrid gets set back to -1 (#5). While we discourage reaching into the insides of the view's child components to directly modify properties in your production code, we really don't have a choice when it comes to testing the view.
10.4.3 Testing refresh issues button click Now that we have the public API tested, we still need to test that certain events get fired when things such as button clicks occur.
The first of these tests, testing for the "Refresh
Issues" button click is shown in Listing 10.11.
Listing 10.11 Test Refresh Issues button click ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
221
... [Test(async)] public function testClickRefreshIssuesButton():void { Async.proceedOnEvent(this, eventDispatcher, UIEvent.REFRESH_ISSUES_BUTTON_CLICKED, 5000); target.refreshIssuesButton.dispatchEvent( new MouseEvent(MouseEvent.CLICK));
#1
#2 #3
} ... 1 Test annotation 2 proceedOnEvent 3 Simulate mouse click
This is a fairly simple test because we don't have to do any kind of setup to run the test. We start out by annotating the test with the [Test] annotation and add the extra bit of metadata to tell FlexUnit that this test will utilize the asynchronous features (#1). Next we set the expectation for the REFRESH_ISSUES_BUTTON_CLICKED event (#2).
Lastly we
simulate our button click by having the refreshIssuesButton dispatch a MouseEvent of type CLICK. That's all there is too it. Next we're going to test something a little more complex, the item click on the DataGrid.
10.4.4 Testing DataGrid item select The last test we're going to write for the view before we move on to writing tests for the model is for when a user clicks on an item in the DataGrid.
This test will require a little bit
more setup than the last test we wrote, as we need to have some data in our DataGrid in order for our event handler to work. Listing 10.12 shows the code for this test.
Listing 10.12 Test DataGrid item select [Test(async)] public function testDataGridItemSelect():void { Async.proceedOnEvent(this, eventDispatcher, UIEvent.SELECTED_ISSUE_CHANGED, 5000); var dataProvider:ArrayCollection = new ArrayCollection(); var issue1:Issue = new Issue(); issue1.id = 1; dataProvider.addItem(issue1);
#1
#2 #3
var issue2:Issue = new Issue(); issue2.id = 5; dataProvider.addItem(issue2); target.issues = dataProvider;
#3
target.masterViewDataGrid.dispatchEvent( new ListEvent(ListEvent.ITEM_CLICK, false, false, 1, 2));
#4
} ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
222
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
1 Test annotation 2 Expected event 3 Sample data for DataGrid 4 Simulate ItemClick
Again we start out by annotating our test with the [Test] annotation and adding the async parameter (#1). Next we set our expectation that the SELECTED_ISSUE_CHANGED event will get dispatched (#2). Now we need to create an ArrayCollection and populate it with a couple of Issue objects so we can set the dataProvider property on the DataGrid (#3) so that the DataGrid will contain the item that we say we're going to click on. Lastly we simulate the user clicking on an item in the DataGrid by having it dispatch a ListEvent of type ITEM_CLICK (#4).
The other parameters in the constructor for the ListEvent determine whether the
event should bubble up through the parent containers, whether or not it's cancellable, the column that we clicked, and lastly which row we clicked on.
So in this instance we're
simulating the user clicking on the first column of the second row of the DataGrid. Now you should be able to run the tests again by opening up a command line, navigating to your project folder and typing mvn test. After all of the messages scroll by you should hopefully be presented with a message saying all tests passed. If not, the results of the unit tests are located in the target/surefire-reports directory. Now that we've got the view tests working, let's move on to the model tests.
10.5 Testing the Model Now that we've got the presenter tests as well as the view tests well under way, it's time to look at writing tests for the model. The tests we'll be writing for the model will be somewhat similar to how we tested the presenter in that we'll be testing the interaction between the model and the RemoteService by utilizing mock objects and setting expectations on the executions performed on them. We'll start out just like we did for the presenter and view tests by creating the test class, which is shown in Listing 10.13.
Listing 10.13 IssueModelTest.as package org.foj.model { ... public class IssueModelTest { private private private private private
var var var var var
target:IssueModel; service:MockIssueService; token:AsyncToken; responder:IResponder; mockery:Mockery;
[Before(async,timeout=5000)] public function setUp():void { mockery = new Mockery(); mockery.prepare(MockIssueService); mockery.prepare(AsyncToken); mockery.prepare(IResponder);
#1
#1 #2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
223
Async.proceedOnEvent(this, mockery, Event.COMPLETE, 5000);
#2
} [After] public function tearDown():void { mockery.verify(service, token); }
#3
} } 1 Private fields 2 Setup method 3 Teardown method
This test class starts much like the presenter test.
We start by defining some private
fields for our target class we're testing, the mocks that we'll need to interact with, and our mock control (#1). Then inside of our setUp method, we instantiate our mock control and prepare the various mocks that we'll be using, and set the expectation to only allow the test to continue when the COMPLETE event is dispatched, ensuring that our mocks are ready to be used (#2). Lastly we define our tearDown method and put a call to the verify method on our mock control inside (#3).
10.5.1 Mocking and RemoteObject Before we can get started writing our tests for the IssueModel, there is a slight caveat that we need to be aware of regarding the RemoteObject class.
As we've mentioned before,
ActionScript is a fairly powerful dynamic language. For those of you familiar with Groovy or Ruby you will likely be familiar with the concept of having the ability to create some really fluent domain specific languages (DSLs) by leveraging the method missing functionality. ActionScript also has this ability, and the AbstractService class, which all of your remoting components HTTPService, WebService, and RemoteObject derive from, takes advantage of this to allow you to call the remote object methods as if they were defined by these remoting components.
ActionScript dynamic classes, proxies and method missing If you would like to learn more about how to use dynamic classes and implement something like method_missing in ActionScript there is a good article on the FlexOnRails blog at http://flexonrails.net/?p=95, which should be able to get you started.
The problem with this arises when you try to mock calls to these dynamic methods that don't truly exist in the RemoteObject class. It would appear that there is a limitation on how the mocking framework instruments and creates your mock objects for you, that only the methods that exist in the class definition that you are mocking are able to be called. If the methods don't exist, the method call will get captured by the callProperty method and exhibit the same dynamic behavior that it would in your production code, which is not what we want.
So in order to get past this, we will define a stub class that will extend the
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
224
Allmon and Anderson / Flex on Java
RemoteObject class.
Last saved: 1/6/2010
Inside of this class we will stub out the methods that our remote
service exposes, and then we'll mock this MockIssueService class inside of our tests so we can properly set and verify our expectations.
Listing 10.14 MockIssueService package org.foj.model { import mx.rpc.AsyncToken; import mx.rpc.remoting.RemoteObject; public class MockIssueService extends RemoteObject {
#1
public function getAll():AsyncToken { return null; }
#2
public function get(id:*):AsyncToken { return null; }
#2
public function save(issue:*):AsyncToken { return null; }
#2
public function remove(id:*):AsyncToken { return null; }
#2
} } 1. Extends RemoteObject 2. Stubbed methods
Listing 10.14 shows the code for our MockIssueService class.
We start out our class
definition by extending RemoteObject so that we can use this to create a mock and pass it to our IssueModel when we instantiate it in our test (#1). Next we stub out the methods that we call on our RemoteObject inside the IssueModel, and just stub them to return null (#2). It really doesn't matter what we put in these methods because we'll be using the mock framework to define our expectations anyway.
Listing 10.15 updated IssueModel constructor public function IssueModel(issueService:RemoteObject = null) { if (issueService != null) { _issueService = issueService; } else { var defaultChannelSet:ChannelSet = ChannelSetFactory.getDefaultChannel(); _issueService = new RemoteObject(); _issueService.destination = "issueService"; _issueService.channelSet = defaultChannelSet; ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
225
} } We also need to update the constructor on the IssueModel class to enable you to inject our mock RemoteObject via the constructor. Listing 10.15 shows the updated constructor on the IssueModel.
10.5.2 Test get all issues Now we're ready to write our IssueModel tests.
Let's start out by testing the getIssues
method, which is probably the most called method in our IssueModel. Listing 10.16 shows the code for our first model test.
Listing 10.16 getIssues test [Test] public function getAllIssues():void { service = mockery.strict(MockIssueService) as MockIssueService; token = mockery.strict(AsyncToken)as AsyncToken; responder = mockery.strict(IResponder) as IResponder;
#1 #1
mockery.mock(service).method("getAll") .withNoArgs.returns(token).once; mockery.mock(token).method("addResponder") .withArgs(responder).once;
#2
target = new IssueModel(service); target.getIssues(responder);
#3
} 1 Mocks 2 Mock expectation 3 Call getIssues
We start this test first by creating the mocks we need for this test, a mock of our MockIssueService that we just defined, a mock AsyncToken, and a mock IResponder (#1). Next we define our expectations on the mocks, starting with the expectation to call the "getAll" method with no arguments, and returning the mock AsyncToken we just created. Then we expect that the mock AsyncToken's addResponder method will be called with the mock IResponder we created (#2) and use to also call the method we are testing (#3). Next let's look at testing the getIssue method.
10.5.3 Test get single issue The next method we'll test on the IssueModel is the getIssue method, which takes in an issue ID as the first parameter to look up an issue by its ID. It's only slightly more involved than the test we just wrote, but still very easy to follow. Listing 10.17 shows the code for this next test.
Listing 10.17 getIssue test ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
226
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
[Test] public function getIssue():void { service = mockery.strict(MockIssueService) as MockIssueService; token = mockery.strict(AsyncToken)as AsyncToken; responder = mockery.strict(IResponder) as IResponder; var id:Number = 42;
#1 #1 #2
mockery.mock(service).method("get").withArgs(id).returns(token).once; #3 mockery.mock(token).method("addResponder").withArgs(IResponder).once; #3 target = new IssueModel(service); target.getIssue(id, responder);
#4
} 1 Mocks 2 ID parameter 3 Mock expectations 4 Call getIssue
We start this test the same way we did the getIssues test, by creating all of the mocks that we need for this test (#1). We then define a local variable to contain the id parameter (#2) that we'll call the method we're testing with. Next we define our expectations on the mocks (#3), which look similar to the getIssues test expectations, except for this time we are expecting the "get" method on our RemoteObject to be called with the id parameter we defined above.
Lastly we instantiate our IssueModel with our mock service and call the
getIssue method (#4). There, we now have a good start on our unit tests for this project.
If you're feeling
ambitious go ahead and continue down the road of writing more tests for the project. Then when you're finished come back and we'll setup a continuous integration server using Hudson, or come back after you've got Hudson setup, it's your choice.
10.6 Continuous integration with Hudson When a team of 2 or more of developers works together, an immediate need arises for frequent feedback on how things are going, as changes are committed between team members. This is where the practice of Continuous Integration (CI) comes in to play. Tooling, like Hudson, really becomes a third party and impartial member of a team. A good CI server provides teams with essential feedback through the orchestration of critical project events as they pertain to the building and stability of the source code. It also can provide a unique look into the health of an application. In this section, a Hudson CI server will be configured to do the following:
Listen for changes in the version control system
Automatically build the project from the top-level pom when changes are discovered
Execute and report on unit test execution
Fail builds upon build errors or failed tests
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
227
Send email out to team members upon failed or corrected builds
There are several benefits for setting up a CI server with even the most basic configuration. How a team configures and uses a CI server depends on what the application does and what kind of feedback is necessary to ensure that everything is in a healthy state. From code being checked into the version control system to the final verification of the deployment and everything in between are all important. There are several plugins that can be configured with Hudson for different purposes. To understand what kind of CI configuration best suites a team's needs a team may consider jotting down a list of questions that may be important things to know everyday during development.
What does the application do?
What are the user stories? (test coverage?)
Does the source code compile when integrated?
Do all the unit tests run successfully?
Is the code quality acceptable? (Hudson has plugins for code quality tools like PMD, FindBugs, and other analysis tools)
Is there an unhealthy volume of “TODO” and “FIXME” annotations in the code and what are they? (Hudson has a task scanner plugin that performs static analysis and creates visibility for these in Java code comment annotations along with unhealthy thresholds settings.)
Does the application deploy successfully?
How do we verify the application and the environment(s) it has to run inside?
Do integration, functional, FIT, and any other post deployment styled tests run successfully?
Now that we have a feel for why we would want to have a CI environment let's move on to getting a sample Hudson instance set up for demonstration.
10.6.1 Downloading and installing Hudson Installing Hudson couldn't be more trivial. Hudson is a simple Java web application and downloads as a war file. To download Hudson go to http://hudson-ci.org. There you will find the “Latest and greatest” link for downloading the most current release. Hudson requires JRE 1.5 or later and can be started standalone, without an application server, by invoking the command “java -jar hudson.war”, where the hudson.war file was installed. This will fire up Hudson inside of an embedded Winstone servlet container on port 8080 by default. Hudson can also be installed into a fixed servlet container that supports Servlet 2.4/JSP 2.0 or later, such as Glassfish, Tomcat, JBoss, and Jetty.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
228
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
If problems arise during installation, consult the Hudson website for help. Let's move on to configuring the server.
10.6.2 Configuring Hudson If Hudson is installed correctly you should be able to see the start screen. For our sample we are using the embedded Winstone server. Open up a web browser and navigate to http://localhost:8080. Once there, Hudson will present an initial start page with no jobs configured as seen in figure 10.2.
Figure 10.2 Hudson start screen
The first thing to do is configure Hudson globally by selecting the “Manage Hudson” link. There we can globally configure Ant, Maven, and JDK versions. Configuring things globally will make them available for all jobs we create. Figure 10.3 displays the Hudson management selection screen.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
229
Figure 10.3 The Hudson management selection screen
From the management screen select the “Configure System” link at the top of the list of options. It's also possible to manage plugins, view system information, and view logs from this top-level screen. Once in the configuration screen we can begin to add the version of Maven and Java that we'd like to use globally. Figure 10.4 displays the configuration screen.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
230
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 10.4 Hudson configuration screen.
Considering our list of goals to accomplish for the demonstration, from the Hudson configuration screen we want to configure a version of Maven, a JDK, and email notification. Finally, we will save it from there. Configuring maven is as easy as selecting a version and instructing Hudson to either automatically install as seen in figure 10.5 or simply pointing Hudson to an already installed Maven location.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
231
Figure 10.5 Maven automatic installation selection
Use the default to install Maven automatically and enter a name for the install to something meaningful for the version. Choose the version and installation will happen. To select a version of Maven from a local installation deselect the “Install automatically” checkbox. Figure 10.6 displays the result.
Figure 10.6 Maven local installation
Enter the MAVEN_HOME, as seen in Figure 10.6, by pointing to the directory where Maven is installed. That's it! As you can see it's possible to continue adding more Maven installations simply by selecting the “Add Maven” button. Next, configure the JDK version used to compile the Java source code. It's possible that the Maven pom files will override this but Hudson build server must contain the version required by the pom. The JDK configuration works identical to the Maven configuration.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
232
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Finally, setting up the mail settings can be done by specifying a SMTP server, a default domain suffix, a system admin email address, and Hudson URL, as seen in Figure 10.7.
Figure 10.7 Hudson email notification
Having email configured will allow Hudson to communicate to teams when build failures or corrections occur. Now that Hudson is ready let's move on to creating a job for the FlexBugs application.
10.6.3 Configuring a Hudson job Configuring a job for Hudson is just as simple as configuring Hudson itself. In fact, Hudson has good support for Maven projects and handles multi-module projects with elegance by allowing you to drill down into the specific modules to gain more fine-grained visibility. To create a new job in Hudson select the “New Job” link on the left-hand side of the screen. Hudson will present options for what type of job to create along with the ability to enter a name for the job and to copy configuration from another job, if one exists. Figure 10.8 displays this initial screen.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
233
Figure 10.8 Hudson job configuration start screen
To demonstrate Hudson we will create a job for the Flex-Bugs application and select a maven 2 project. After selecting the “ok” button Hudson will present the job configuration screen. This is where we will configure access to the source control system and other settings that will accomplish our basic goals for building and providing feedback to the team when changes are checked into the source control system. On the job configuration screen we will mostly go with the default settings and start by pointing
Hudson to the source control
management
system Subversion (SVN)
on
googlecode.com. Figure 10.9 displays the settings that will accomplish this along with settings to check for changes every 5 minutes. This will cause a build to happen.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
234
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Figure 10.9 Source code management section
The next thing to do is configure the job with the needed Maven goals. Figure 10.10 displays this part of the screen. It's good to note that you can point Hudson to a pom in any directory or even to a Maven pom other than something named “pom.xml.” The MAVEN_OPTS may need to be configured to increase the memory size for running inside the standalone Winstone server. e.g. -Xms256m -Xmx768m -XX:MaxPermSize=512m.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
235
Figure 10.10 The build instructions and email notification settings
For Flex-Bugs we need to call the clean install goals at the top-level pom. This will invoke the maven build for all modules as usual. We also added the -e for outputting stacktrace information and also set up email notification. The email notification will use the default mail settings we established when configuring Hudson global settings. Once these settings are saved we can start the build by selecting the “Build Now” link. From there you have a build started that will check out the source code and build the entire application while providing feedback on the status and emails on build failures or corrections. That's all there is to it with configuring a CI Server from the ground up! As you can see there are all kinds of options with Hudson and various ways to execute a job.
10.7 Summary In this chapter we introduced you to unit testing using FlexUnit4 and the mock-as3 mock testing framework.
We also reiterated the reasoning behind the Model View Presenter
pattern that we introduced way back in Chapter 4.
We covered many of the common
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
236
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
patterns that we would have needed to test in our sample application. With the knowledge presented in this chapter, you should be able to continue writing tests for the rest of the functionality in the application. Finally, we setup and configured a CI server for providing critical feedback on how things are going as code is integrated into the source code repository. Hudson is a great choice for teams needing a reliable CI server. Hudson has a lot of built-in capabilities and a vibrant user community with several plugins for just about anything. Lastly we're going to take a look at how you can quickly get a Flex application integrated with the Grails framework.
We'll quickly introduce a very simple contact management
application, and integrate it with Grails as well as JMS.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
237
11 Flex on Grails
This chapter covers
Integrating Flex with Groovy and Grails
How to install the Grails Flex Plugin
How to expose a Grails service to Flex
Integrating Flex with JMS
Not all real-world development is against an existing application. Every once in a while we get to have some fun and develop new code. So what do you do when you want to rapidly prototype a data driven application with Flex? Well, the Flex part is easy enough to rapidly develop a user interface, but what about the backend?
You could always develop a Java
based backend using the same methods and techniques we described throughout this book, or you can take advantage of another framework, such as Grails, to quickly get your data driven backend off the ground.
11.1 Why Groovy and Grails? Groovy has been affectionately called Java 2.0 by many in the Java world, so it should be no surprise that we can integrate with Groovy and Grails just as easily as we did in previous chapters, and in some ways easier.
Groovy is Java 2.0 "Groovy is a lot like Java 2.0, if someone set out to completely rewrite the Java language today. Rather than replacing Java, Groovy complements it, providing a simpler, slicker syntax where the type checking is done dynamically at runtime. You can use Groovy to write Java applications on the fly, to glue together Java modules, or even to extend existing Java applications — you can even use Groovy to unit test your Java code. And
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
238
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
the beauty of it is, Groovy lets you do all these things faster — sometimes a lot faster — than you would if you were writing pure Java code." Andrew
Glover
from
his
Fluently
Groovy
series
on
IBM
DeveloperWorks
(http://www.ibm.com/developerworks/edu/j-dw-java-jgroovy-i.html)
Right about now you may be wondering to yourself “Why Flex and Grails?” To which I respond, “Why not?” In this final chapter we’re going to demonstrate how you can quickly build and prototype data enabled Flex applications leveraging the rapid development provided by Groovy and Grails coupled with the powerful Grails plugin for Flex.
Whether
your intent is to just spike out a particular piece of functionality or to build a complete application, there are few options that will allow you to develop as rapidly as with Grails. We’re going to assume that you as the reader are at least somewhat familiar with both Groovy and Grails, but if you’ve never seen or done any development using either, don’t worry you should still be able to follow along. We’ll show some idiomatic Groovy code but won’t go into too much detail about what the code does as that is beyond the scope of this chapter. If you want to learn more about Groovy we would suggest starting with the Groovy homepage (http://groovy.codehaus.org) or by reading the book Groovy in Action by Dierk Koenig (http://www.manning.com/koenig/).
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
239
Figure 11.1 The sample application
In this chapter we’ll be building a very simplified contact management application called “Flex Contacts”. We’ll build a single Master-Detail screen that we can use to keep track of our contacts shown in Figure 11.1. Before we can get started writing our application, you'll need to install Grails.
11.2 Downloading and installing Grails Installing Grails is actually a rather simple task. In order to install Grails, point your browser to the Grails project downloads page (http://grails.org/Download) and download the appropriate distribution for your platform. This chapter was written using Grails 1.1.2, which was the latest stable distribution available at the time. There is really no reason to install Groovy separately since it is included with the Grails distribution. Grails is available in two main forms, a binary distribution and a source distribution.
Go ahead and download the
binary distribution in either Zip or Tar/GZ format, depending on what operating system you're using. After you've downloaded the binary distribution for Grails, unzip the Grails distribution to a folder such as c:\dev\grails-1.1.2. Then the next step is to create a GRAILS_HOME environment variable and point it to the folder you just extracted Grails to. Lastly append
GRAILS_HOME\bin to your PATH variable so that you can run the Grails executable from the command line. Once you’ve finished with that, you can open up a command line and type grails –version and you should see output similar to what's shown below. C:\dev> grails -version Welcome to Grails 1.1.2 - http://grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: C:\dev\grails-1.1.2 Base Directory: C:\dev ... Congratulations you’ve now got Grails installed and configured. Now let's move on to creating the Grails application.
11.3 Creating the Grails application Here’s where we see one of the first examples of Grails’ “convention over configuration” way of doing things.
In order to create our Grails application, we simply have to open a
command line and navigate to the folder where we want to create our Grails application, for example c:\dev\projects\ and type the following command. C:\dev\projects\> grails create-app flex-contacts With just that one simple command Grails has enough information to generate our project for us. No hard to remember or cryptic command line parameters, just one simple ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
240
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
concise command and Grails creates the project structure and installs all the necessary dependencies. We don’t even have to install a database server or servlet container to run the application, that’s all included out of the box when you create your Grails application. If you thought that was easy, wait till you see how easily we can add functionality to your Grails application using plugins, but first, let's continue building the Grails application by starting with defining the domain model.
11.3.1 Create the Contact domain model Next we’ll create our domain class that will be responsible for holding contact information. For this simple example we’ll only need one domain object called “Contact”. Go ahead and create the Contact object by issuing the following command: C:\dev\projects\flex-contacts\> grails create-domain-class contact This will create our Contact domain model class in the grails-app/domain folder within our flex-contacts project.
In our Contact domain model, we’re going to add a few simple
properties for persisting our contact information such as first name, last name, and address information, as well as some simple validation constraints. Listing 11.1 shows our completed contact.groovy file.
Listing 11.1 contact.groovy class Contact { static contstraints = { firstName(blank: false, minLength: 4, maxLength: 15) lastName(blank: false, minLength: 4, maxLength: 25) address(blank:false) city(blank:false) state(blank: false, length: 2) zipCode(blank: false, minLength: 5, maxLength: 10) }
#1
String String String String String String
#2
firstName lastName address city state zipCode
#1
#2
String toString() { return firstName + " " + lastName }
#3
} 1 Constraints 2 Properties 3 toString()
The Contact class contains just a few fields that we’ll need to hold things like first name, last name, and address (#2).
We’ve also defined a few constraints (#1) so that we can
validate that we get all the information we need from the front end. We've also implemented ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
241
a toString() (#3) method in order to provide a more meaningful implementation than the default. Now let's create the service that we'll be exposing to the Flex application.
11.3.2 Create the ContactService In order to expose our Grails application to our Flex front-end, we need to create a service that will expose the functionality for our application to perform CRUD operations on our Contact domain object. To do this we issue the following command on the command line: C:\dev\projects\flex-contacts> grails create-service contact This will create a class named ContactService in the grails-app/services folder. The ContactService will contain the methods we’ll be exposing to Flex to consume as a remote service.
Inside the ContactService.groovy file we’ll implement just a few simple methods to
enable our Flex application to get a list of all the contacts in the database, get a specific contact, save a contact, and to delete a contact from the database. Listing 11.2 shows the completed ContactService class.
Listing 11.2 ContactService.groovy class ContactService { static expose = ['flex-remoting']
#1
boolean transactional = true
#2
def getContacts() { return Contact.list() }
#3
def get(id) { return Contact.get(id) }
#3
def update(Contact contact) { contact.save() }
#3
def remove(Contact contact) { contact.delete(flush: true) }
#3
} 1 Expose to Flex 2 Transactional property 3 Service methods
Most of the ContactService class should look familiar to you. The only line that may look a little odd is the line that contains the code static expose = ['flex-remoting'] (#1). This single line of code is really all you need to expose this service to Flex to be able to call any one of the service methods from our Flex application. We also make all of the service methods (#3) in our service transactional by setting the transactional property to true (#2). The Flex plugin for Grails keeps true to the Grails philosophy of Convention over ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
242
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
Configuration in that it abstracts much of the configuration that you would have needed to do had this been a Java application leveraging either BlazeDS or LiveCycle Data Services to expose this functionality.
11.3.3 Bootstrap some sample data The last thing we want to do before diving in to building the Flex client is to bootstrap our application with some sample data so that we have some contact information in the database when we first run it. This will also allow us to see that the flex remoting is working correctly. To do this we add the code shown in Listing 11.3 to the BootStrap.groovy file in the grailsapp/conf folder of our application.
Listing 11.3 BootStrap.groovy class BootStrap { def init = {servletContext -> Contact contact1 = new Contact(firstName: "Jeremy", lastName: "Anderson", address: "123 Main St", city: "Jenison", state: "MI", zipCode: "49428") contact1.save()
#1
Contact contact2 = new Contact(firstName: "BJ", lastName: "Allmon", address: "234 Any St", city: "Delaware", state: "OH", zipCode: "43015") contact2.save()
#1
} def destroy = { } } 1. Sample data
Bootstrapping data in Grails allows us to have some data injected into our application each time we restart it. We do this by creating a couple of Contact objects (#1) in our BootStrap.groovy file and call save() on them to persist them to the database.
This is a
helpful feature of Grails as we move through development and beats having to manually enter contacts every time.
You would typically use this file for bootstrapping any kind of
initial data in your application, such as states and state codes. Now we're ready to begin developing the Flex frontend for the Grails application we just created.
11.4 Getting Rich with Flex Now that we have our Grails application created we can move on with the task of creating the Flex client that will integrate with Grails. We'll start out by installing the Flex plugin for Grails, then add the Flex application to the Grails project.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
243
11.4.1 Installing the Flex Plugin From the root of the project directory enter the following command to install the Flex plugin for the Grails application: C:\dev\projects\flex-contacts\> grails install-plugin flex The previous command will pull down all of the Flex libraries our application needs to be able to compile the Flex application, which may take some time because this plugin has to pull down a lot of dependencies. Once all the messages have scrolled by, the plugin should be successfully installed.
Like many other features in Grails development, enabling an
application for Flex integration is extremely simple and declarative.
There are no
configuration files that need to be created, though there are some configuration files contained within the web-app/WEB-INF folder if you have a need to fine-tune the flex compiler settings.
11.4.2 Creating the domain classes in Flex We can begin the Flex development by creating a domain object in ActionScript. This object will act as a Data Transfer Object of sorts. This will allow us to deal with full-fledged objects when our service returns data to the client. This approach avoids having to deal with the pseudo proxy objects that Flex would wrap our objects into if it didn’t have anything to translate it into. When we installed the flex plugin in the previous step, it created a flex folder under
web-app/WEB-INF.
Inside of this folder resides another folder called user-classes,
which contains a file that clues us into where we’re supposed to place our ActionScript classes appropriately called add_your_as_and_swc_files_here. Create a new file in the
user-classes folder called Contact.as. Listing 11.4 shows the completed class. Listing 11.4 Contact.as package { [Bindable] [RemoteClass(alias = "Contact")] public class Contact {
#1 #2
public function Contact() { } public var id:*; public var version:*;
#3 #3
public public public public public public
#4
var var var var var var
firstName:String lastName:String address:String city:String state:String zipCode:String
#4
} } ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
244
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
1 Bindable 2 RemoteClass 3 Hibernate specific properties 4 Public properties
The code in Listing 11.4 shouldn’t look completely alien to you. It should resemble the client side domain classes that we created earlier for our Flex Bugs application. The first two items to take note of are the annotations at the top of the file [Bindable] (#1) and [RemoteClass] (#2).
The Bindable annotation lets Flex know that whenever one of the
values changes in this object that it should notify anything else that is bound to this object should update itself. We didn't utilize data binding in our other application, however for this quick example we decided to in order to keep the examples shorter. In case you have forgotten, the RemoteClass annotation allows a Flex class to be mapped to a server-side class. The Contact.as class is mapped to the Contact.groovy domain class we created earlier. Since we’re not making use of a package structure in this trivial example we don’t need to fully qualify it here, however if our domain object in Grails did fall under a specific package, we would have to fully qualify that object in this annotation in order for it to work correctly. There are a couple of Hibernate specific properties (#3) that we need to add to our Contact domain class in order for our application to behave as intended, along with all of the normal public properties (#4) that we need to include for the data fields we want to be able to persist.
11.4.2 Creating the Flex application Now that we’ve got our domain object created for the client side, the next thing we’ll need to create is a file which will contain the main Flex application itself, which will be called main.mxml and needs to be created in the web-app folder of our Grails application. We’ll break this down into bite-sized chunks that should hopefully be easier to digest than if you were simply presented with the end state of the application.
First we’ll start by
creating the Application object. Just like in our other sample application, our Flex application will have the Application as its root node of the MXML file as shown below. Now that we’ve got the base application started, we’re ready to start laying out the basic layout. DEFINING THE LAYOUT Take a look at Figure 11.1 again, and take note that we’ve got 3 main containers in use for this application. We’ve got the header at the top containing the text “Flex Contacts on Grails”, and two panels containing the master view and the details view. Let’s go ahead and ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
245
create the basic layout for our application now. To do that we’re going to leverage a couple of layout components: HBox and Panel.
Listing 11.6 Main.mxml continued
#1
#2
#3
1 Header container 2 Master view 3 Detail view
Listing 11.6 shows the code used to generate the layout of our application.
We’ve
specified our Application to use “vertical” as it’s main layout method back in Listing 11.5 so that our components will flow vertically down as we add them to the Application. Here we start out by adding an HBox container (#1) containing a single Text component inside which prints our title out in the header. Next we wrap two Panel containers in another HBox so that they’ll show up side by side in our application. These two Panel components will house our master view (#2) and detail view (#3) respectively. CREATING THE MASTERVIEW Inside of our first Panel container we’ll create our master view using the DataGrid component, which allows us to display tabular data rather painlessly.
Listing 11.7 Main.mxml continued
#1
#2 #2 ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
246
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
#3
#3
1 DataProvider 2 DataGrid columns 3 ControlBar
Listing 11.7 shows the code necessary to create the contacts DataGrid as well as define the columns that we want it to display. You may have noticed the items contained within the curly braces {}, this is how you specify data binding in a Flex application. We’ve bound the dataProvider property (#1) of the DataGrid to the contactsList variable, which we’ll define in a minute. This is where we’re going to store the results from our RemoteObject method call to get all the contacts. You may also notice that all of our column names (#2) should match up with what we’ve defined in our Contact object we created earlier.
This allows Flex to
automagically figure out which data field to map to which column. Lastly, we create a ControlBar(#3), which will contain all of the buttons we’ll need to interact with this control. CREATING THE DETAIL VIEW Now that we’ve got the master view created, it’s on to the detail view. The detail view will provide a mechanism for editing and updating of our contacts. While it’s true we could have allowed editing right in the DataGrid itself, it wouldn’t have made for a very good example and doesn’t quite show off the power of data binding in Flex.
Listing 11.8 Main.mxml continued #1 #2 #2 ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
247
#3
#3
1 The Form 2 FormItems 3 ControlBar
Listing 8 shows the code for our detail view.
In this we leverage another container
object, the Form component (#1). Unlike its HTML counterpart, the Form component in Flex is strictly a container. You do not need to have your fields wrapped in a Form component for you to POST data to the server-side. Inside of the Form component we define a series of FormItem components (#2) that contain the actual GUI form components we’ll use for data entry. These should be fairly selfexplanatory.
Take note of the data binding syntax in the text attributes for these
components. This indicates that we’ll be binding the text values of these components to a variable called selectedContact. Lastly we add another ControlBar (#3) just like we did for the master view to contain any buttons we need to control the application.
11.4.3 Adding the RemoteService Flex has a few different components that enable it to communicate with the server-side, namely HTTPService, WebService, and RemoteService. In a nutshell WebService facilitates easy communication with SOAP based Web Services. HTTPService allows you to consume and call other web services using a variety of protocols such as XML and JSON and is the best choice if you’re trying to interact with some sort of RESTful resource. RemoteService leverages Adobe’s binary AMF (ActionScript Message Format) protocol, which tends to give the best performance of the three.
Listing 11.9 Adding the RemoteService Listing 11.9 shows the code necessary for us to communicate with our remote service we defined earlier.
As we stated earlier we’re going to use the RemoteObject component to
facilitate communication with the server-side, and we’ve defined the methods that we’ll be calling so that we can define our callback methods that we’ll be using to handle the results coming back from the server as well as any faults.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
248
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
11.4.4 Putting it all together We’re almost done. All that’s left is to put it all together and add some methods that we’ll call to handle different events from the user interface. Listing 10 contains the code that we’ll need to finish off the application.
Listing 11.10 Main.mxml
#1
#2
#3
#4
#4
1 Contact 2 RemoteObject event handler 3 RemoteObject fault handler 4 Event handlers
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
Most of what is shown in Listing 11.10 should make sense.
249
Here we define a Contact
object in MXML rather than ActionScript (#1). Notice that we’re also performing data binding back to the form components, creating a 2 way binding between the selectedContact variable and the detail form. Next we define the event handler (#2) and fault handler (#3) for our RemoteObject that we defined in Listing 11.9. Lastly we have all the event handlers for the components in the master view and detail view (#4). Now that we’ve completed this part of the application, you can startup your Grails application by executing grails run-app and navigating to the application. application
is
running,
simply
fire
up
your
browser
and
Once your
navigate
to
http://localhost:8080/flex-contacts/main.mxml and you should see something that should slightly resemble Figure 11.1.
Now you've got a functional Flex application running on
Grails. Over the next few sections we're going to modify this simple example to leverage JMS and ActiveMQ.
11.5 Install the Grails JMS and ActiveMQ plugins The next thing we're going to demonstrate is the ability for our Grails application to push data out to the Flex application using some Flex components that integrate with JMS to produce and consume messages. This will allow us to remove the refresh button and some of the plumbing involved to refresh the Flex DataGrid when a user would add, edit, or delete a contact. This helps to clean up the client a bit by reducing the amount of view logic and complexity, which is obviously a good thing. In order to install the Grails JMS plugin from the root of the project directory: $ grails install-plugin jms Then do the same for the ActiveMQ plugin: $ grails install-plugin activemq Once these plugins are installed we are ready for development. The beauty of Grails conventions is that it hides most of the complexity of “plugging in” new external frameworks such as JMS and ActiveMQ. It's worth mentioning that the JMS and ActiveMQ plugins are still fairly new but seem to do the job for the most common situations. Now that we have the plugins we'd like to use for making the application JMS-enabled, we can move on to updating the Grails application code. There are only a few things that will need to happen in order to provide a JMS service to the Flex client which are described next.
11.6 Add the ActiveMQ Spring Bean There's one configuration detail we need to tend to. We need to configure the JMS plugin to leverage the ActiveMQ broker. We can do this either by adding a Spring bean to the resources.xml or by adding the bean using the Groovy DSL approach. Listing 11.11 demonstrates adding the bean by using the DSL approach by adding our connection factory as a bean in the resources.groovy configuration file located in the grails-app/conf/spring folder of the application. // Place your Spring DSL code here beans = { ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
250
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
connectionFactory(org.apache.activemq.ActiveMQConnectionFactory) { brokerURL = "vm://localhost" } } The code shown above is the Groovy way to configure Spring beans in Grails, and defines a connectionFactory bean of type ActiveMQConnectionFactory and initializes its brokerURL property to point at vm://localhost. Now that we got the configuration out of the way we can move on to tweaking the Contact domain class. class Contact implements Serializable { ... } In order to store messages on a message queue the objects need to implement the Serializable interface. So we need to update the Contact class to implement the Serializable interface as shown above.
11.7 Subscribe the Flex client to the Grails JMS service Now we are ready to configure the Flex framework as a JMS consumer. First we'll start with the BlazeDS configuration.
11.7.1 Update the services-config.xml We need to configure Flex with a contactsTopic in the top-level BlazeDS configuration file. When the Flex plugin is installed it places the services-config.xml inside the /web-
app/WEB-INF/flex directory. Let's edit it by adding the Flex message service inside the services element. The full services-config.xml is included in Listing 11.13 for clarity and to show that the order of the bean definitions has significance. Inside the services element the grails service comes first followed by the JMS configuration.
Listing 11.13 services-config.xml
#1 #1 #1 #2 #2 #2 #2
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
contacts javax.jms.ObjectMessage ConnectionFactory NON_PERSISTENT DEFAULT_PRIORITY AUTO_ACKNOWLEDGE false Context.PROVIDER_URL vm://localhost Context.INITIAL_CONTEXT_FACTORY
251
#2 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3 #3
org.apache.activemq.jndi.ActiveMQInitialContextFactory #3 #3 #3 topic.contacts #3 contacts #3 #3 #3 #3 #3 #3
1 JMS service 2 The JMS adapter 3 JMS configuration
The bulk of the services-config.xml file shown in Listing 11.13 was generated for us when we installed the Flex plugin, however we need to add a new service section for the JMS service that we're going to be using (#1), and a section stating that we'd like the service to use the JMS adapter (#2). We also need to configure the JMS topic that we'll be communicating with (#3). If you'd like to know what all of these options are that we've defined here, refer to the ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
252
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
BlazeDS user documentation at http://livedocs.adobe.com/blazeds/1/blazeds_devguide/. Now let's move on to modifying out ContactService to utilize our messaging.
11.7.2 Modifying the ContactService The existing ContactService class is relatively simple. Listing 11.14 shows the updated ContactService class with the new additions for publishing the updated contact list.
Listing 11.14 ContactService updated class ContactService { static expose = ['flex-remoting'] boolean transactional = true def getContacts() { return Contact.list() } def get(id) { return Contact.get(id) } def update(Contact contact) { contact.save() publishContacts() } def remove(Contact contact) { contact.delete(flush: true) publishContacts() } def private void publishContacts() { try { sendPubSubJMSMessage(“contacts”, getContacts()); } catch (Exception e) { log.error("Failed to publish contacts.", e); } }
#1
#1
#2
} 1 publishContacts 2 sendPubSubJMSMessage
In Listing 11.14 we add the publishContacts()(#1) method to publish updates to the topic we configured in the services-config.xml file. The sendPubSubJMSMessage
(#2) takes two arguments. The first argument is the JNDI destination name defined in our topic and the other is the list of contacts. Next, we wire up the update and remove methods to publishContacts when they are invoked. There are other methods that can be called depending on your needs. Since a topic supports the publish/subscribe model it is used for one-to-many messaging which works well with our contacts application. For one-to-one or
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
Last saved: 1/6/2010
Allmon and Anderson / Flex on Java
253
point-to-point messaging you would instead use a queue. To learn more about the JMS plugin visit the Grails website at http://www.grails.org/JMS+Plugin.
11.7.3 Update the Main.mxml The final thing we need to do before relaunching the contacts application is to update the Flex client main.mxml file. We will add the JMS service to the mix and make some other minor changes. Let's start with the Application element.
Listing 11.15 Main.mxml
#2
... 1 creationComplete 2 Consumer
As seen in Listing 11.15, the Flex Consumer object (#2) is used in order to connect to the contactsTopic. When the application initializes we need to subscribe to the contactsTopic, to do so we configure the creationComplete event definition (#1) to call subscribe on the jmsConsumer. The consumer listens for changes from the ActiveMQ broker and uses the handleGetContacts method to consume the data. This method updates the contact list when there are updates. From here, the application will work just fine “as-is”. However, there are some things that we should clean up since making the application JMS aware because they are unnecessary overhead.
Listing 11.16 Main.mxml private function deleteContact(selectedContact:Contact):void { contactService.remove(selectedContact); } private function updateContact(contact:Contact):void { contactService.update(selectedContact); } ... ©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455
Licensed to Wow! eBook
254
Allmon and Anderson / Flex on Java
Last saved: 1/6/2010
We want to remove the calls to the getContacts() method on our RemoteObject when deleteContact and updateContact are invoked as seen in Listing 11.16. These calls were necessary, prior to enabling JMS, in order for the DataGrid to be refreshed whenever the contact list was updated. Finally, we can also remove the refresh button. The Flex consumer will get updates every few seconds and will invoke the method that will update the DataGrid so there's no more need for these extra calls to the server side. The consumer itself can be further configured if different timing is needed or other options. To illustrate the push of information from the server to the client start your Grails application by opening up a command line and navigating to the project folder.
Type the
command grails run-app, which will start the embedded jetty container to run the application, now open up two browsers and point them both to the Flex application at http://localhost:8080/flex-contacts/main.mxml.
Then as you make updates in the one
window, you should see the contacts being updated in the other browser window. No more having to manually refresh the application to pick up other user's changes.
11.8 Summary In this final chapter we showed you how to rapidly prototype data enabled Flex applications using Groovy and Grails in combination with the Flex plugin for Grails.
We started off by
defining our domain in Grails and exposing some services for our Flex application to use, and of course the Flex application itself.
We then went one step further and enabled our
application to use JMS and ActiveMQ for real-time updating of our user interface.
©Manning Publications Co. Please post comments or corrections to the Author Online forum: http://www.manning-sandbox.com/forum.jspa?forumID=455